我如何实现"op"操作?就"op ="而言在CRTP基类中? [英] How can I implement "op" in terms of "op=" in a CRTP base class?

查看:122
本文介绍了我如何实现"op"操作?就"op ="而言在CRTP基类中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

赫伯·萨特(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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆