Herb Sutter的Guru of the Week #4, "Class Mechanics"教导说,重载运算符的“a op b”形式应该用“a op = b来实现“表格(见解决方案中的第4点)。
作为一个例子,他展示了如何为+
运算符做到这一点:
T& T::operator+=( const T& other ) {
//...
return *this;
}
T operator+( T a, const T& b ) {
a += b;
return a;
}
他指出operator+
中的第一个参数是故意按值传递的,因此如果调用者传递临时值,则可以移动它。
请注意,这要求operator+
是非成员函数。
我的问题是,如何将此技术应用于CRTP基类中的重载运算符?
所以说这是我的CRTP基类及其operator+=
:
template <typename Derived>
struct Base
{
//...
Derived operator+=(const Derived& other)
{
//...
return static_cast<Derived&>(*this);
}
};
如果我省略“按值传递第一个参数”优化,我可以看到如何用operator+
作为成员函数实现operator+=
:
template <typename Derived>
struct Base
{
//...
Derived operator+(const Derived& other) const
{
Derived result(static_cast<const Derived&>(*this);
result += other;
return result;
}
};
但是有没有办法在使用优化时执行此操作(从而使operator+
成为非成员)?
答案 0 :(得分:3)
实施Herb建议的正常方法如下:
struct A {
A& operator+=(cosnt A& rhs)
{
...
return *this;
}
friend A operator+(A lhs, cosnt A& rhs)
{
return lhs += rhs;
}
};
将其扩展到CRTP:
template <typename Derived>
struct Base
{
Derived& operator+=(const Derived& other)
{
//....
return *self();
}
friend Derived operator+(Derived left, const Derived& other)
{
return left += other;
}
private:
Derived* self() {return static_cast<Derived*>(this);}
};
如果你试图避免在这里使用friend
,你会发现它几乎这个:
template<class T>
T operator+(T left, const T& right)
{return left += right;}
但仅对从Base<T>
派生的内容有效,这是非常棘手和丑陋的。
template<class T, class valid=typename std::enable_if<std::is_base_of<Base<T>,T>::value,T>::type>
T operator+(T left, const T& right)
{return left+=right;}
此外,如果它是类的内部friend
,那么它在技术上并不在全局命名空间中。因此,如果有人写了无效的a+b
,其中两者都不是Base
,那么您的超载不会导致1000行错误消息。免费的类型 - 特质版本。
<小时/> 至于为什么签名:mutable,const&amp;的值。对于不可变的。 &安培;&安培;实际上只适用于移动构造函数和其他一些特殊情况。
T operator+(T&&, T) //left side can't bind to lvalues, unnecessary copy of right hand side ALWAYS
T operator+(T&&, T&&) //neither left nor right can bind to lvalues
T operator+(T&&, const T&) //left side can't bind to lvalues
T operator+(const T&, T) //unnecessary copies of left sometimes and right ALWAYS
T operator+(const T&, T&&) //unnecessary copy of left sometimes and right cant bind to rvalues
T operator+(const T&, const T&) //unnecessary copy of left sometimes
T operator+(T, T) //unnecessary copy of right hand side ALWAYS
T operator+(T, T&&) //right side cant bind to lvalues
T operator+(T, const T&) //good
//when implemented as a member, it acts as if the lhs is of type `T`.
如果移动比副本快得多,并且您正在处理交换运算符,那么可能有理由将这四个重载。但是,仅适用于可交换运算符(其中A?B == B?A,所以+和*,但不适用于 - ,/或%)。对于非交换运算符,没有理由不使用上面的单个重载。
T operator+(T&& lhs , const T& rhs) {return lhs+=rhs;}
T operator+(T&& lhs , T&& rhs) {return lhs+=rhs;} //no purpose except resolving ambiguity
T operator+(const T& lhs , const T& rhs) {return T(lhs)+=rhs;} //no purpose except resolving ambiguity
T operator+(const T& lhs, T&& rhs) {return rhs+=lhs;} //THIS ONE GIVES THE PERFORMANCE BOOST