我们的项目中有一个特殊的接口框架,部分要求是代表接口的类只能用作虚拟基类,而不能用作非虚拟基类。有没有办法在代码中强制执行此操作?也就是说,如果类是从非虚拟派生的,则会产生编译错误。
我可以访问VS 2010实现的C ++ 11:这意味着static_assert
,enable_if
和<type_traits>
可用。
答案 0 :(得分:3)
IMO,此问题没有可用的清洁和平台独立解决方案。
最好的方法是手动将每个继承更改为virtual
继承
要做到这一点,识别接口的派生类(比如class Base
)很容易(!)。可以遵循以下步骤:
class Base
设为final
(c ++ 11);即class Base final { ...
virtual
final
关键字并成功编译代码每当您想要进行这种健全性检查时,必须定期跟踪此过程(不幸的是)。
答案 1 :(得分:3)
可以在编译时检查。关键是,如果我们有钻石图案:
您可以明确地将D&
投射到A&
。但是,如果继承是非虚拟的:
演员会模棱两可。所以,让我们试着制作一颗钻石!
template <typename Base, typename Derived>
class make_diamond {
struct D2 : virtual Base { }; // this one MUST be virtual
// otherwise we'd NEVER have a diamond
public:
struct type : Derived, D2 { };
};
此时它只是另一个void_t
- 样式类型特征:
template <typename Base, typename Derived, typename = void>
struct is_virtual_base_of : std::false_type { };
template <typename Base, typename Derived>
struct is_virtual_base_of<Base, Derived, void_t<
decltype(static_cast<Base&>(
std::declval<typename make_diamond<Base, Derived>::type&>()))
>> : std::true_type { };
如果强制转换是明确的,则部分特化中的表达式将有效,并且该特化将是首选。如果演员阵容不明确,我们将会有替换失败,并最终成为主要的。请注意,Base
这里实际上不需要任何virtual
成员函数:
struct A { };
struct B : public A { };
struct C : virtual A { };
std::cout << is_virtual_base_of<A, B>::value << std::endl; // 0
std::cout << is_virtual_base_of<A, C>::value << std::endl; // 1
如果它有任何纯虚拟成员函数,我们不必覆盖它们,因为我们从未真正构造一个对象。
struct A2 { virtual void foo() = 0; };
struct B2 : public A2 { void foo() override { } };
struct C2 : virtual A2 { void foo() override { } };
std::cout << is_virtual_base_of<A2, B2>::value << std::endl; // 0
std::cout << is_virtual_base_of<A2, C2>::value << std::endl; // 1
当然,如果您的课程被标记为final
,那么这根本不起作用。但是,如果它是final
,无论如何它都具有什么样的继承性并不重要。
答案 2 :(得分:1)
有趣的问题。您可以通过隐藏接口类并暴露从接口虚拟继承的具体类来接近您想要的内容。这显然需要一些解决方法和尴尬,但它可能适合您的需求。这是一个例子:
#include <iostream>
using namespace std;
class Hide {
struct VInterface {
void foo() const { cout << "VInterface::foo()\n"; }
VInterface const &as_interface() const { return *this; }
protected:
virtual ~VInterface() { }
};
public:
struct VBase : virtual VInterface {
};
};
typedef Hide::VBase VBase;
struct VDiamond1 : VBase { };
struct VDiamond2 : VBase { };
struct VConcrete : VDiamond1, VDiamond2 { };
int main() {
VConcrete vc;
auto const &vi = vc.as_interface();
vi.foo();
}
有可能使用可用于继承的decltype()
和as_interface()
来重建名称,但是我尝试的那些导致编译器错误导致析构函数受到保护,所以我希望如果它是可能的,它至少相对困难,可能足以满足您的需求。