Guidelines to do constexpr operator-overloading?
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.
TemplateRex
Hobbyist C++ programmer. c++ gold badge #191 c++11 gold badge #18 templates silver badge #22 c++-faq: When do extra parentheses have an effect, other than on operator precedence? c++-faq: How to implement classic sorting algorithms in modern C++?
Updated on June 07, 2022Comments
-
TemplateRex almost 2 years
Consider a simple int
Wrapper
class with overloaded multiplicationoperator*=
andoperator*
. For "old-style" operator-overloading, one can defineoperator*
in terms ofoperator*=
, 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. Aconstexpr operator*
cannot calloperator*=
because the latter modifies its (implicit) left argument. Furthermore, there is no overloading on constexpr, so adding an extraconstexpr operator*
to the existingoperator*
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:
- duplication of the multiplication logic in both
operator*=
andoperator*
, instead ofoperator*
being expressed in terms ofoperator*=
- hence, Boost.Operators no longer works to reduce the boilerplate for writing many other arithmetic operators
Question: is this the recommended C++11 way of having both a run-time
operator*=
and mixed run-time/compile-timeconstexpr 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; }
- duplication of the multiplication logic in both