就现代 C++ 中的 operator+= 而言,对称 operator+?
Symmetric operator+ in terms of operator+= in modern C++?
我正在阅读这篇关于 Boost.Operator https://www.boost.org/doc/libs/1_69_0/libs/utility/operators.htm#symmetry 中对称运算符实现的说明,我怀疑它已经过时了。
讨论围绕什么是一般实施 operator+
的最佳方式展开,如果一致的 operator+=
可用的话。结论是它是(曾经),
T operator+( const T& lhs, const T& rhs ){
T nrv( lhs ); nrv += rhs; return nrv;
}
因为当时一些编译器支持 NRVO,而不是 RVO。
现在, 强制执行 NRVO, 进行了各种优化,现在还是这样吗?
例如,现在对某些情况可能有意义的其他版本是:
T operator+(T lhs, const T& rhs ){
T ret(std::move(lhs)); ret += rhs; return ret;
}
或
T operator+(T lhs, const T& rhs ){
lhs += rhs; return lhs;
}
给定一个 class,它有一个构造函数、一个移动构造函数和合理的 operator+=
。例如:
#include<array>
#include<algorithm>
using element = double; // here double, but can be more complicated
using array = std::array<double, 9>; // here array, but can be more complicated
array& operator+=(array& a, array const& b){
std::transform(begin(a), end(a), begin(b), begin(a), [](auto&& x, auto&& y){return x + y;});
return a;
}
array& operator+=(array&& a, array const& b){
std::transform(begin(a), end(a), begin(b), begin(a), [](auto&& x, auto&& y){return x + std::move(y);});
return a;
}
实现对称 operator+
的最佳方法是什么?这是一组可能的代码
/*1*/ array sum(array const& a, array const& b){array tmp(a); tmp+=b; return tmp;} // need operator+= and copy-constructor
/*2*/ array sum(array const& a, array const& b){return array(a)+=b;} // needs operator+= && and copy-constructor
/*3*/ array sum(array a, array const& b){return std::move(a)+=b;} // needs operator+= && and can use move-constructor
/*4*/ array sum(array a, array const& b){array tmp(std::move(a)); tmp+=b; return tmp;} // needs operator+= and can use move-constructor
我在 https://godbolt.org/z/2YPhcg 中尝试过,只是通过计算装配线的数量,在所有其他条件相同的情况下,这可能会告诉我们什么是最佳实施。我得到这些混合结果:
| code | gcc -O2 | clang -O2 |
|:-----------|------------:|:------------:|
| /*1*/ | 33 lines | 64 lines |
| /*2*/ | 39 lines | 59 lines |
| /*3*/ | 33 lines | 62 lines |
| /*4*/ | 33 lines | 64 lines |
虽然 /*3*/
和 /*4*/
可以受益于 sum(std::move(a), b)
甚至 sum(sum(a, c), b)
.
形式的调用
那么 T tmp(a); tmp+=b; return tmp;
仍然是实施 operator+(T [const&], T const&)
的最佳方式吗?
看起来如果有移动构造函数和移动+=,还有其他可能性,但似乎只能在 clang 中产生更简单的组装。
如果签名是:
T operator+(T const& a, T const& b )
(如您在粗体问题文本中所说),则正文应为:
return T(a) += b;
其中 结果对象 是唯一构造的 T
。版本 T nrv( lhs ); nrv += rhs; return nrv;
理论上存在编译器不将 nrv
与 结果对象 .
合并的风险
注意上面的签名不允许移出任何参数。如果希望搬出 lhs,那么这对我来说似乎是最佳选择:
T operator+(T const& a, T const& b)
{
return T(a) += b;
}
T operator+(T&& a, T const& b)
{
return T(std::move(a)) += b;
}
在这两种情况下,结果对象 保证是唯一构造的对象。在采用 T a
的 "classic" 版本中,右值参数的情况会导致额外的移动。
如果您想移出右侧,则可以再添加两个重载:)
注意我没有考虑为reasons described here
返回T&&
的情况
我正在阅读这篇关于 Boost.Operator https://www.boost.org/doc/libs/1_69_0/libs/utility/operators.htm#symmetry 中对称运算符实现的说明,我怀疑它已经过时了。
讨论围绕什么是一般实施 operator+
的最佳方式展开,如果一致的 operator+=
可用的话。结论是它是(曾经),
T operator+( const T& lhs, const T& rhs ){
T nrv( lhs ); nrv += rhs; return nrv;
}
因为当时一些编译器支持 NRVO,而不是 RVO。
现在, 强制执行 NRVO, 进行了各种优化,现在还是这样吗?
例如,现在对某些情况可能有意义的其他版本是:
T operator+(T lhs, const T& rhs ){
T ret(std::move(lhs)); ret += rhs; return ret;
}
或
T operator+(T lhs, const T& rhs ){
lhs += rhs; return lhs;
}
给定一个 class,它有一个构造函数、一个移动构造函数和合理的 operator+=
。例如:
#include<array>
#include<algorithm>
using element = double; // here double, but can be more complicated
using array = std::array<double, 9>; // here array, but can be more complicated
array& operator+=(array& a, array const& b){
std::transform(begin(a), end(a), begin(b), begin(a), [](auto&& x, auto&& y){return x + y;});
return a;
}
array& operator+=(array&& a, array const& b){
std::transform(begin(a), end(a), begin(b), begin(a), [](auto&& x, auto&& y){return x + std::move(y);});
return a;
}
实现对称 operator+
的最佳方法是什么?这是一组可能的代码
/*1*/ array sum(array const& a, array const& b){array tmp(a); tmp+=b; return tmp;} // need operator+= and copy-constructor
/*2*/ array sum(array const& a, array const& b){return array(a)+=b;} // needs operator+= && and copy-constructor
/*3*/ array sum(array a, array const& b){return std::move(a)+=b;} // needs operator+= && and can use move-constructor
/*4*/ array sum(array a, array const& b){array tmp(std::move(a)); tmp+=b; return tmp;} // needs operator+= and can use move-constructor
我在 https://godbolt.org/z/2YPhcg 中尝试过,只是通过计算装配线的数量,在所有其他条件相同的情况下,这可能会告诉我们什么是最佳实施。我得到这些混合结果:
| code | gcc -O2 | clang -O2 |
|:-----------|------------:|:------------:|
| /*1*/ | 33 lines | 64 lines |
| /*2*/ | 39 lines | 59 lines |
| /*3*/ | 33 lines | 62 lines |
| /*4*/ | 33 lines | 64 lines |
虽然 /*3*/
和 /*4*/
可以受益于 sum(std::move(a), b)
甚至 sum(sum(a, c), b)
.
那么 T tmp(a); tmp+=b; return tmp;
仍然是实施 operator+(T [const&], T const&)
的最佳方式吗?
看起来如果有移动构造函数和移动+=,还有其他可能性,但似乎只能在 clang 中产生更简单的组装。
如果签名是:
T operator+(T const& a, T const& b )
(如您在粗体问题文本中所说),则正文应为:
return T(a) += b;
其中 结果对象 是唯一构造的 T
。版本 T nrv( lhs ); nrv += rhs; return nrv;
理论上存在编译器不将 nrv
与 结果对象 .
注意上面的签名不允许移出任何参数。如果希望搬出 lhs,那么这对我来说似乎是最佳选择:
T operator+(T const& a, T const& b)
{
return T(a) += b;
}
T operator+(T&& a, T const& b)
{
return T(std::move(a)) += b;
}
在这两种情况下,结果对象 保证是唯一构造的对象。在采用 T a
的 "classic" 版本中,右值参数的情况会导致额外的移动。
如果您想移出右侧,则可以再添加两个重载:)
注意我没有考虑为reasons described here
返回T&&
的情况