我如何实现"op"操作?就"op ="而言在CRTP基类中? [英] How can I implement "op" in terms of "op=" in a CRTP base class?
问题描述
赫伯·萨特(Herb Sutter)的本周大师#4,类力学" 教导说,重载运算符的"a op b"形式应以"a op = b"形式实现(请参阅第#点解决方案中的4).
Herb Sutter's Guru of the Week #4, "Class Mechanics", teaches that the "a op b" form of an overloaded operator should be implemented in terms of the "a op= b" form (see point #4 in the solutions).
作为一个例子,他展示了如何对+
运算符执行此操作:
As an example, he shows how do to this for the +
operator:
T& T::operator+=( const T& other ) {
//...
return *this;
}
T operator+( T a, const T& b ) {
a += b;
return a;
}
他指出,operator+
中的第一个参数是有意通过值传递的,因此,如果调用方传递了一个临时值,则可以移动它.
He points out that first parameter in operator+
is intentionally being passed by value, so that it can be moved if the caller passes a temporary.
请注意,这要求operator+
是非成员函数.
Notice that this requires that the operator+
be a non-member function.
我的问题是,如何将这种技术应用于 CRTP中的重载运算符基类?
My question is, how can I apply this technique to an overloaded operator in a CRTP base class?
所以说这是我的CRTP基类及其operator+=
:
So say this is my CRTP base class with its operator+=
:
template <typename Derived>
struct Base
{
//...
Derived operator+=(const Derived& other)
{
//...
return static_cast<Derived&>(*this);
}
};
如果我省去了按值传递第一个参数"的优化,我可以看到如何将operator+
作为成员函数实现为operator+=
:
I can see how to implement operator+
in terms of operator+=
as a member function if I dispense with the "pass the first argument by value" optimization:
template <typename Derived>
struct Base
{
//...
Derived operator+(const Derived& other) const
{
Derived result(static_cast<const Derived&>(*this);
result += other;
return result;
}
};
但是在使用该优化时是否有办法做到这一点(并因此使operator+
成为非成员)?
but is there a way to do this while using that optimization (and therefore making the operator+
a nonmember)?
推荐答案
实施Herb的建议的常规方法如下:
The normal way to implement Herb's advice is as follows:
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
,您会意识到这几乎是 :
If you try to avoid the use of friend
here, you realize it's almost this:
template<class T>
T operator+(T left, const T& right)
{return left += right;}
但是仅对从Base<T>
派生的东西有效,这样做很棘手而且很丑.
But is only valid for things derived from Base<T>
, which is tricky and ugly to do.
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行错误消息.免费的类型特征版本可以.
Additionally, if it's a friend
internal to the class, then it's not technically in the global namespace. So if someone writes an invalid a+b
where neither is a Base
, then your overload won't contribute to the 1000 line error message. The free type-trait version does.
至于该签名的原因:可变,const&的值;为一成不变. &&确实仅适用于move构造函数和其他一些特殊情况.
As for why that signature: Values for mutable, const& for immutable. && is really only for move constructors and a few other special cases.
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,所以+和*,而不是-,/或%).对于非交换运算符,没有理由不使用上面的单个重载.
If moves are much faster than copies, and you're dealing with a commutative operator, you may be justified in overloading these four. However, it only applies to commutative operators (where A?B==B?A, so + and *, but not -, /, or %). For non-commutative operators, there's no reason to not use the single overload above.
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
这篇关于我如何实现"op"操作?就"op ="而言在CRTP基类中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!