Reputation: 70526
Consider a simple int Wrapper
class with overloaded multiplication operator*=
and operator*
. For "old-style" operator-overloading, one can define operator*
in terms of operator*=
, and there are even libraries like Boost.Operators and its modern incarnation df.operators by @DanielFrey that reduce the boilerplate for you.
However, for compile-time computations using the new C++11 constexpr
, this convenience disappears. A constexpr operator*
cannot call operator*=
because the latter modifies its (implicit) left argument. Furthermore, there is no overloading on constexpr, so adding an extra constexpr operator*
to the existing operator*
results in an overload resolution ambiguity.
My current approach is:
#include <iostream>
struct Wrap
{
int value;
Wrap& operator*=(Wrap const& rhs)
{ value *= rhs.value; return *this; }
// need to comment this function because of overloading ambiguity with the constexpr version
// friend Wrap operator*(Wrap const& lhs, Wrap const& rhs)
// { return Wrap { lhs } *= rhs; }
friend constexpr Wrap operator*(Wrap const& lhs, Wrap const& rhs)
{ return { lhs.value * rhs.value }; }
};
constexpr Wrap factorial(int n)
{
return n? factorial(n - 1) * Wrap { n } : Wrap { 1 };
}
// want to be able to statically initialize these arrays
struct Hold
{
static constexpr Wrap Int[] = { factorial(0), factorial(1), factorial(2), factorial(3) };
};
int main()
{
std::cout << Hold::Int[3].value << "\n"; // 6
auto w = Wrap { 2 };
w *= Wrap { 3 };
std::cout << w.value << "\n"; // 6
}
Live output here. My problems with this are:
operator*=
and operator*
, instead of operator*
being expressed in terms of operator*=
Question: is this the recommended C++11 way of having both a run-time operator*=
and mixed run-time/compile-time constexpr operator*
? Does C++14 change anything here to e.g. reduce the logic duplication?
UPDATE: The answer by @AndyProwl is accepted as idiomatic but as per suggestion of @DyP, in C++11 one could reduce the logic duplication at the expense of an extra assignment and counter-intuitive style
// define operator*= in terms of operator*
Wrap& operator*=(Wrap const& rhs)
{ *this = *this * rhs; return *this; }
Upvotes: 29
Views: 14748
Reputation: 126442
I could not find an idiomatic solution for C++11 (although as a workaround, DyP's suggestion seems acceptable to me).
In C++14 however, where constexpr
does not imply const
(see Annex C.3.1 of the C++14 Standard Draft n3690), you could simply define both operator *=
and operator *
as constexpr
, and define the latter in terms of the former, as usual:
struct Wrap
{
int value;
constexpr Wrap& operator *= (Wrap const& rhs)
{ value *= rhs.value; return *this; }
friend constexpr Wrap operator * (Wrap const& lhs, Wrap const& rhs)
{ return Wrap(lhs) *= rhs; }
};
Here is a live example, where the above program is being compiled with -std=c++1y
on Clang - unfortunately, GCC does not seem to implement this rule yet.
Upvotes: 24