I have the following template class:
template
class T : public I
{
// ...
};
This template class need to be derived once (an
This is possible if the base class T
knows the types of its derived classes. This knowledge can be passed by CRTP or by an overloading tag to its constructor. Here's the latter case:
template
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." );
Then, T::T( Derived * )
needs to do something that will cause a problem if it has two specializations (with different Derived
). Friend functions are great for that. Instantiate an auxiliary, non-member class depending on
, with a friend
function that depends on T
but not Derived
.
T_Derived_reservation< T, Derived >{};
}
};
Here's the auxiliary class. (Its definition should come before T
.) First, it needs a base class to allow ADL on T_Derived_reservation< T, Derived >
to find a signature that doesn't mention 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 > ) {}
};
It would be nice to get a link error when two .cpp
files declare different incompatible derived classes, but I can't prevent the linker from merging the inline functions. So, the assert
will fire instead. (You can probably manage to declare all the derived classes in a header, and not worry about the assert
firing.)
Demo.
You've edited to say that T
cannot know its derived types. Well, there's nothing you can do at compile time, since that information is simply unavailable. If T
is polymorphic, then you can observe the dynamic type to be the derived class A
or B
, but not in the constructor or destructor. If there's some other function reliably called by the derived class, you can hook into that:
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.
}
};