假设我有一个基类,以及它的衍生两类:
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) {}
};
我要做到以下几点:
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;
}
换句话说,我要保证遗传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);
}
...
};
但是,这只是一种痛苦。 此外,它似乎并不像正确的代码重用我。
什么是这里使用正确的技术?
如果你能确保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
};
( final
是一个C ++ 11功能,可防止进一步的推导。)
如果允许从派生Derived1
和Derived2
,那么你得到的麻烦:
class Derived3 : public Derived1 {};
Derived3 d3;
Derived1 d1;
Derived1& d3_disguised = d3;
d1 + d3_disguised; // oooops, this is allowed
有没有办法防止这种在编译时。 而且,即使你想让它,它是不容易得到体面的语义无本经营的多分派 。
您可以使用专门的模板函数添加值。 遗憾的是这一招不与运营商合作:如果类型不相同,就返回正确的类型时失败,并且:
#include <type_traits>
class Base;
template <class Derived>
Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);
class Base
{
...
template <class Derived>
friend Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);
};
template <class Derived>
Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL)
{
return l.value + r.value;
}
并证明它的工作原理:
int main() {
int a = 0;
a = a + a;
Derived1 d11(0), d12(0);
Derived2 d21(0), d22(0);
add(d11, d12);
add(d21, d22);
add(d12, d22); // here it fails to compile...
}
只要value
仅被定义在基类中,操作并不需要访问任何派生成员,你也许可以逃脱只定义了基地运营,让隐式类型铸造处理其余部分。 对于不同类型的错误,它可能是值得一小的牺牲使用基于枚举系统跟踪类型,然后做一个简单的比较,以检查是否有无效的条件。
enum eTypeEnum {BASE, DER1, DER2};
class Base {
public:
virtual ~Base(){}
Base(double value) : eType(BASE),value(value) {}
Base(const Base& B) { value=B.value; }
Base operator+ (const Base& B) const {
if (eType != B.eType) return -1; //error condition
return Base(value+B.value);
}
double getVal(){return value;}
protected:
eTypeEnum eType;
double value;
};
class Derived1 : public Base {
public:
Derived1(double value) : Base(value) {eType = DER1;}
};
class Derived2 : public Base {
public:
Derived2(double value) : Base(value) {eType = DER2;}
};
int main() {
int tmp;
Derived1 a(4.0);
Derived2 b(3.0);
Base c(2.0);
cout << "aa:" << (a+a).getVal(); // 8
cout << "\nbb:" << (b+b).getVal(); // 6
cout << "\nba:" << (b+a).getVal(); // 7
cout << "\nab:"<< (a+b).getVal(); // 7
cout << "\ncc:"<< (c+c).getVal(); // 4
cout << "\nac:"<< (a+c).getVal(); // 6
cout << "\nbc:" << (b+c).getVal(); // 5
cout << "\nabc:" << (a+b+c).getVal();// 9
cout << endl;
cin >> tmp;
return 0;
}
输出:AA:BB 8:6 BA:-1 AB:-1 CC:4 AC:-1 BC:-1 ABC:1
我看到的唯一问题是,链接多个操作在一起时,铸造螺丝了处理。 在这里, a+b+c
432的计算结果为(a+b)+c
所以a+b
位遇到错误条件(返回-1),但被铸造为Base
,它可以让(-1)+c
返回“1 ”。