Ответ 1
Это возможно, если базовый класс 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>
, с функцией 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.
}
};