我有以下模板类:
template<class I>
class T : public I
{
// ...
};
对于给定的模板参数I
,此模板类需要一次(且仅一次)派生。
class A : public T<U> {}; // ok
class B : public T<V> {}; // ok
class C : public T<U> {}; // compile error
可以调整模板类T
以实现此类行为(而类A
,B
,U
,V
不能);但是,T
必须对衍生类A
,B
,C
一无所知。
有没有办法防止这样的模板类被多次派生?理想情况下,在这种情况下发出编译错误,或者至少是链接器错误。
答案 0 :(得分: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." );
然后,T::T( Derived * )
需要做一些会导致问题的事情,如果它有两个特化(具有不同的Derived
)。朋友的功能非常棒。根据{{1}}实例化辅助非成员类,其<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
答案 1 :(得分:5)
我不是宏的忠实粉丝,但如果使用宏对你来说不是问题 - 你可以使用一个简单紧凑的解决方案,如下所示:
#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();
}
答案 2 :(得分:3)
既然你提到可以将A
,B
和C
传递给T
那么这个运行时解决方案怎么样?
#include <cassert>
#include <typeinfo>
//This class will check to see each T<X> is only instantiated with a unique Y
template <class X>
struct T_helper
{
template <class Y>
static void check()
{
if(derived_type)
assert(*derived_type == typeid(Y));
else
derived_type = &typeid(Y);
}
static const std::type_info * derived_type;
};
template <class X>
const std::type_info * T_helper<X>::derived_type = nullptr;
template <class X, class Y>
struct T
{
T()
{
T_helper<X>::template check<Y>();
}
};
struct A : T<int, A> {};
struct B : T<int, B> {};
int main()
{
A a1, a2, a3; // These are all okay
B b1; // This one will trigger the assert
}
答案 3 :(得分:3)
我能想到的唯一答案是拥有实例化及其衍生物的注册表。然后,您可以设计一个搜索注册表的元函数。你传递它的基础和派生,如果你没有使用注册类型派生它返回void类型或其他东西,所以它会导致编译器错误。根据您的要求,基地不知道派生的这是我能想到的唯一可能的答案。
您的声明将如下所示:
struct A : search_registry<T<U>, A>::type { ... };
我相信你会遇到许多难以解决的问题 - 祝你好运!祝你好运!
请记住,模板元程序纯粹是功能性的。你不能添加&#34;以任何好方式到注册表。您需要定义一次以容纳所有内容。从好的方面来说,如果有人忘记添加到他们已经知道的注册表中,或者如果他们使用之前定义的那个......在减号上,那就像鸭子那样丑陋。