Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guidelines to do constexpr operator-overloading?

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:

  • duplication of the multiplication logic in both operator*= and operator*, instead of operator* being expressed in terms of operator*=
  • 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-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; } 
like image 558
TemplateRex Avatar asked Jul 19 '13 11:07

TemplateRex


People also ask

How do you overload an operator?

Operator Overloading in Binary Operators Here, + is a binary operator that works on the operands num and 9 . When we overload the binary operator for user-defined types by using the code: obj3 = obj1 + obj2; The operator function is called using the obj1 object and obj2 is passed as an argument to the function.

What is Constexpr in C ++ 11?

The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors.

How do you define operator overloading in C++?

An overloaded operator is called an operator function. You declare an operator function with the keyword operator preceding the operator. Overloaded operators are distinct from overloaded functions, but like overloaded functions, they are distinguished by the number and types of operands used with the operator.

Which operator Cannot be overloaded C++?

Dot (.) operator can't be overloaded, so it will generate an error.


1 Answers

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.

like image 144
Andy Prowl Avatar answered Oct 08 '22 22:10

Andy Prowl