问题描述
考虑一个简单的 Wrapper 带有重载乘法的类 operator * = 和 。对于旧式运算符重载,可以按运算符* = 定义运算符* 甚至是等图书馆及其通过@DanielFrey的来减少你的样板。
但是,对于使用新的C ++ 11 constexpr 的编译时计算,这种方便就消失了。由于后者修改其(隐式)左参数,因此 constexpr operator * 不能调用 operator * = 。此外,还有,因此添加了一个额外的 constexpr运算符* 到现有的运算符* 会导致重载解析模糊。
我目前的方法是:
#include< iostream>
struct Wrap
{
int value;
Wrap& operator * =(Wrap const& rhs)
{value * = rhs.value; return * this; }
//需要注释此函数,因为使用constexpr版本重载模糊性
// friend Wrap运算符*(Wrap const& lhs,Wrap const& rhs)
/ / {return Wrap {lhs} * = rhs; }
friend constexpr Wrap运算符*(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};
}
//希望能够静态初始化这些数组
struct Hold
{
static constexpr Wrap Int [] = {factorial ),factorial(1),factorial(2),factorial(3)};
};
int main()
{
std :: cout< Hold :: Int [3] .value<< \\\
; // 6
auto w = Wrap {2};
w * = Wrap {3};
std :: cout<< w.value<< \\\
; // 6
}
。我的问题是:
- 运算符* = 运算符* ,而不是运算符* / code>
- 因此,Boost.Operators不再适用于减少编写许多其他算术运算符的样板。
问题:这是推荐的C ++ 11方式,同时具有运行时 operator * = 混合运行时/编译时 constexpr operator * ? C ++ 14改变这里的任何东西。减少逻辑重复?
UPDATE :@AndyProwl的答案被接受为惯用的,但根据@DyP的建议,在C + 11一个 可以减少逻辑重复,但需要额外的分配和反直觉风格。
// define operator * = in operator of operator *
Wrap& operator * =(Wrap const& rhs)
{* this = * this * rhs; return * this; }
在C ++ 14中,(见C ++ 14标准草案n3690的附录C.3.1)可以简单地定义 operator * = 和 operator * 为 constexpr
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; }
};
这是一个,其中上述程序使用 -std = c ++ 1y on Clang - 不幸的是,GCC似乎没有实施这条规则。
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; }
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.
这篇关于指南做constexpr操作符重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!