Я читал эту заметку о реализации симметричных операторов в Boost.Operator https://www.boost.org/doc/libs/1_69_0/libs/utility/operators.htm#symmetry, и я подозреваю, что она ужасно устарела.
Речь идет о том, как лучше всего реализовать operator+
в общем случае, если имеется согласованный operator+=
. Отсюда вывод: (100)
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;
}
Учитывая класс, который имеет конструктор, конструктор перемещения и разумный 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.