指南做constexpr操作符重载? [英] Guidelines to do constexpr operator-overloading?
问题描述
考虑一个简单的 Wrapper
带有重载乘法的类 operator * =
和
。对于旧式运算符重载,可以按运算符* =
定义运算符*
甚至是 Boost.Operators 等图书馆及其通过@DanielFrey的 df.operators 来减少你的样板。
但是,对于使用新的C ++ 11 constexpr
的编译时计算,这种方便就消失了。由于后者修改其(隐式)左参数,因此 constexpr operator *
不能调用 operator * =
。此外,还有在constexpr 上没有重载 ,因此添加了一个额外的 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中, constexpr
并不意味着 const
(见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*=
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-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操作符重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!