I have the following template class:
template<class I>
class T : public I
{
// ...
};
This template class need to be derived once (and only once) for a given template parameter I
.
class A : public T<U> {}; // ok
class B : public T<V> {}; // ok
class C : public T<U> {}; // compile error
Template class T
can be adapted to achieve such a behavior (while classes A
, B
, U
, V
cannot); however, T
must not have any knowledge about derived classes A
, B
, C
.
Is there any way to prevent such a template class from being derived more than once? Ideally issuing a compilation error in such a case or, at least, a linker error.
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 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." );
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 <T, Derived>
, 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.
}
};
Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.
En caso de infracción, por favor [email protected] Eliminar
Déjame decir algunas palabras