shr*_*ike 19 c++ inheritance templates
我有以下模板类:
template<class I>
class T : public I
{
// ...
};
Run Code Online (Sandbox Code Playgroud)
对于给定的模板参数,此模板类需要一次(且仅一次)派生I
.
class A : public T<U> {}; // ok
class B : public T<V> {}; // ok
class C : public T<U> {}; // compile error
Run Code Online (Sandbox Code Playgroud)
模板类T
可以适合于实现这样的行为(而类A
,B
,U
,V
不能); 但是,T
决不能有关于派生类的任何知识A
,B
,C
.
有没有办法防止这样的模板类被多次派生?理想情况下,在这种情况下发出编译错误,或者至少是链接器错误.
Pot*_*ter 17
如果基类T
知道其派生类的类型,则可以这样做.这些知识可以通过CRTP或重载标记传递给它的构造函数.这是后一种情况:
template<class I>
class 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." );
Run Code Online (Sandbox Code Playgroud)
然后,T::T( Derived * )
如果它有两个特化(具有不同的特征Derived
),则需要做一些会导致问题的事情.朋友的功能非常棒.根据<T, Derived>
具有friend
依赖T
但不依赖的函数来实例化辅助非成员类Derived
.
T_Derived_reservation< T, Derived >{};
}
};
Run Code Online (Sandbox Code Playgroud)
这是辅助类.(它的定义应该在之前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 > ) {}
};
Run Code Online (Sandbox Code Playgroud)
当两个.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.
}
};
Run Code Online (Sandbox Code Playgroud)
我不是宏的忠实粉丝,但如果使用宏对你来说不是问题 - 你可以使用一个简单紧凑的解决方案,如下所示:
#include <iostream>
template <class>
struct prohibit_double_inheritance { };
#define INHERIT(DERIVING, BASE) \
template<> struct prohibit_double_inheritance<BASE> { };\
struct DERIVING: BASE
template<class I>
struct T: I
{
// ...
static void do_something() {
std::cout << "hurray hurray!" << std::endl;
}
};
struct U { };
struct V { };
INHERIT(A, T<U>) {
};
//INHERIT(B, T<U>) { // cause redetinition of the struct
//}; // prohibit_double_inheritance<T<U>>
int main() {
A::do_something();
}
Run Code Online (Sandbox Code Playgroud)