如果基类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." );
然后,T::T( Derived * ) 需要做一些会导致问题的事情,如果它有两个专业化(具有不同的Derived)。朋友功能非常适合。实例化一个依赖于<T, Derived> 的辅助非成员类,使用一个依赖于T 但不依赖于Derived 的friend 函数。
T_Derived_reservation< T, Derived >{};
}
};
这是辅助类。 (它的定义应该在T之前。)首先,它需要一个基类来允许T_Derived_reservation< T, Derived >上的ADL找到一个没有提到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 触发。)
Demo.
您已编辑说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.
}
};