导出重载运算符,但仅在相同类型上运行

Rod*_*uis 0 c++ oop code-reuse class operator-overloading

假设我有一个基类和两个派生自它的类:

class Base
{
protected:
    double value;
public:
    virtual ~Base();

    Base(double value) : value(value) {}
    Base(const Base& B) { value=B.value; }

    Base operator+ (const Base& B) const { 
        return Base(value+B.value); 
    }

};

class final Derived1 : public Base {
public:
    Derived1(double value) : Base(value) {}
};

class final Derived2 : public Base {
public:
    Derived2(double value) : Base(value) {}
};
Run Code Online (Sandbox Code Playgroud)

我想完成以下任务:

int main(int argc, char *argv[])
{
    Derived1 a = Derived1(4.0);
    Derived2 b = Derived2(3.0);

    a+a; // this should return a Derived1 object
    b+b; // this should return a Derived2 object

    a+b; // this should FAIL AT COMPILE TIME

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

换句话说,我想保证继承operator+只对与调用实例相同类型的对象进行操作.

我该如何干净利落地做到这一点?我发现自己为每个类重新定义了运算符:

class final Derived1 : public Base {
    ...
    Derived1 operator+ (const Derived1& D1) const {
        return Derived1(value+D1.value);
    }
    ...
};

class final Derived2 : public Base {
    ...            
    Derived2 operator+ (const Derived2& D1) const {
        return Derived2(value+D1.value);
    }
    ...
};
Run Code Online (Sandbox Code Playgroud)

但那只是一种痛苦.此外,它似乎不适合我重复使用代码.

在这里使用的正确技术是什么?

R. *_*des 5

如果你可以确定Derived1并且Derived2是叶子类(即没有其他类可以从它们派生),你可以使用奇怪的重复模板模式来做到这一点:

template <typename T>
class BaseWithAddition : public Base {
    T operator+(T const& rhs) const {
        return T(value + rhs.value);
    }
};

class final Derived1 : public BaseWithAddition<Derived1> {
    // blah blah
};

class final Derived2 : public BaseWithAddition<Derived2> {
    // blah blah
};
Run Code Online (Sandbox Code Playgroud)

(final是一个阻止进一步派生的C++ 11特性.)

如果您允许派生Derived1,Derived2那么您会遇到麻烦:

class Derived3 : public Derived1 {};
Derived3 d3;
Derived1 d1;
Derived1& d3_disguised = d3;
d1 + d3_disguised; // oooops, this is allowed
Run Code Online (Sandbox Code Playgroud)

在编译时无法阻止这种情况.即使您想要允许它,如果没有多次调度,也不容易为此操作获得合适的语义.