考虑一下这个例子。
我有一个容器类(A
),它重载/实现了各种算术运算符(A::negate()
)。
我现在希望创建派生类(B
和C
)。
B
和C
应该由A
实施所有运营商。
但是,这些运算符应该使用派生类对象作为参数。
B::negate
的原型应该是:B B::negate()
,而不是A B::negate()
。
派生类不需要任何自己的字段,但可以实现自己的方法(B::foo()
,C::bar()
)。
要求B
和C
不兼容,即B
对象无法分配给C
对象,或与C
中的任何一个一起使用运营商。
以下是示例代码,我希望它如何工作:
struct A {
int val;
A negate() {
return A{-val};
}
};
struct B: A {void foo(){}};
struct C: A {void bar(){}};
int main() {
B obj0 = {5};
B obj1 = obj0.negate();
}
我明白使用标准继承可能是不可能的,而且可能是C ++ 11根本无法做到的,所以我要求尽可能接近它。
我提出的当前最佳解决方案涉及根本不使用继承,而是将整数模板参数添加到基类,将派生类定义为using B = A<1>;
,using C = A<2>;
,并实现仅适用于某些特殊化的成员方法(仅A::foo<1>(){}
和A::bar<2>(){}
)。
但是,我对这个解决方案非常不满意。
答案 0 :(得分:3)
template<typename Child>
struct A {
Child* self() {
static_assert( std::is_base_of< A<Child>, Child >::value, "CRTP failure" );
return static_cast<Child*>(this);
}
Child const* self() const {
static_assert( std::is_base_of< A<Child>, Child >::value, "CRTP failure" );
return static_cast<Child const*>(this);
}
Child negate() {
return Child{-val};
}
};
struct B:A<B> {
explicit B(int v):A<B>(v) {}
};
在这里,我们将有关其子节点的信息注入基类template
。 B
然后可以相对自由地成为正常的班级。
在父级中,您可以self()
获取this
指针作为B
(或其他派生类)。
另一种方法涉及自由功能。您编写了一个免费的negate
template
函数,用于检查其参数是否来自A
,如果是,则返回否定操作,并返回传入类型的否定版本。
这些的混合也有效,你的自由函数需要A<D>
并返回D
。
答案 1 :(得分:0)
如果您不想使用模板:
使A :: negate()受到保护。
在B:
struct B : public A
{
B & negate()
{
A:negate();
return *this
}
/// and so on
}
因为否定在B中定义,它完全隐藏了A中定义的否定,因此B实现被调用并且可以委托给A.
如果您打算隐藏用户的所有A,那么B应该包含A而不是继承它。 (让A ::再次公开)
struct B
{
private:
A m_a;
public:
B & negate()
{
m_a.negate();
return *this
}
/// and so on
}
答案 2 :(得分:0)
协变返回类型:
#include <iostream>
struct A {
virtual A& operator ! () { std::cout << "A" << std::endl; return *this; }
};
struct B : public A {
virtual B& operator ! () { std::cout << "B" << std::endl; return *this; }
};
int main() {
B b;
A* a = &b;
! *a;
}