我有以下模板类:
templateclass T : public I { // ... };
对于给定的模板参数,此模板类需要一次(且仅一次)派生I
.
class A : public T {}; // ok class B : public T{}; // ok class C : public T {}; // compile error
模板类T
可以适合于实现这样的行为(而类A
,B
,U
,V
不能); 但是,T
决不能有关于派生类的任何知识A
,B
,C
.
有没有办法防止这样的模板类被多次派生?理想情况下,在这种情况下发出编译错误,或者至少是链接器错误.
如果基类T
知道其派生类的类型,则可以这样做.这些知识可以通过CRTP或重载标记传递给它的构造函数.这是后一种情况:
templateclass T : public I { protected: template< class Derived > T( Derived * ) { static_assert ( std::is_base_of< T, Derived >::value, "Be honest and pass the derived this to T::T." );
然后,T::T( Derived * )
如果它有两个特化(具有不同的特征Derived
),则需要做一些会导致问题的事情.朋友的功能非常棒.根据
具有friend
依赖T
但不依赖的函数来实例化辅助非成员类Derived
.
T_Derived_reservation< T, Derived >{}; } };
这是辅助类.(它的定义应该在之前T
.)首先,它需要一个基类来允许ADL T_Derived_reservation< T, Derived >
找到一个没有提到的签名Derived
.
template< typename T > class T_reservation { protected: // Make the friend visible to the derived class by ADL. friend void reserve_compile_time( T_reservation ); // Double-check at runtime to catch conflicts between TUs. void reserve_runtime( std::type_info const & derived ) { #ifndef NDEBUG static std::type_info const & proper_derived = derived; assert ( derived == proper_derived && "Illegal inheritance from T." ); #endif } }; template< typename T, typename Derived > struct T_Derived_reservation : T_reservation< T > { T_Derived_reservation() { reserve_compile_time( * this ); this->reserve_runtime( typeid( Derived ) ); } /* Conflicting derived classes within the translation unit will cause a multiple-definition error on reserve_compile_time. */ friend void reserve_compile_time( T_reservation< T > ) {} };
当两个.cpp
文件声明不同的不兼容派生类时,获得链接错误会很好,但我不能阻止链接器合并内联函数.所以,assert
意志会反过来.(您可以设法在标头中声明所有派生类,而不用担心assert
触发.)
演示.
您已编辑过说T
无法知道其派生类型.好吧,在编译时你无能为力,因为这些信息根本不可用.如果T
是多态的,那么你可以观察动态类型是派生类A
或B
构造函数和析构函数,但不是.如果派生类可靠地调用了其他一些函数,您可以将其挂钩:
template< typename I > class T { protected: virtual ~ T() = default; something_essential() { #ifndef NDEBUG static auto const & derived_type = typeid( * this ); assert ( derived_type == typeid( * this ) && "Illegal inheritance from T." ); #endif // Do actual essential work. } };
我不是宏的忠实粉丝,但如果使用宏对你来说不是问题 - 你可以使用一个简单紧凑的解决方案,如下所示:
#includetemplate struct prohibit_double_inheritance { }; #define INHERIT(DERIVING, BASE) \ template<> struct prohibit_double_inheritance { };\ struct DERIVING: BASE template struct T: I { // ... static void do_something() { std::cout << "hurray hurray!" << std::endl; } }; struct U { }; struct V { }; INHERIT(A, T) { }; //INHERIT(B, T) { // cause redetinition of the struct //}; // prohibit_double_inheritance > int main() { A::do_something(); }
[现场演示]