我想,关于 CRTP 的问题相当简单,但我似乎找不到答案。大概是因为太简单了,所以没有人想到去问。我对这个概念很陌生,所以,请不要笑得太厉害;)。
这是代码(有点像尝试做一些类似于 STL 容器的事情):
template< typename tT >
struct TBase
{
typedef tT T;
};
template< typename tTBase >
struct TTraitsBase
{
typedef typename tTBase::T T;
};
template< typename tTHelpee, typename tTTraits >
struct THelper
{
typedef typename tTTraits::T T;
typedef typename tTHelpee::T Th; /* This generates a compiler error:
'T' not being a member of TDerived<tT> */
T Foo( void )
{
return static_cast< tTHelpee* > ( this )->mVal;
}
};
template< typename tT >
struct TDerived : TBase< tT > , THelper< TDerived< tT > , TTraitsBase< TBase< tT > > >
{
using TBase< tT >::T;
T mVal;
};
int main()
{
TDerived< int >::T lTmp = -1;
TDerived< int > lObj;
lObj.mVal = -1;
std::cout << lObj.Foo() << std::endl;
return 0;
}
如果我评论有问题的内容,一切都会编译成功
typedef typename _THelpee::T Th;
。让我困惑的是:如果编译器不喜欢 typedef typename _THelpee::T Th;
,为什么它会让 static_cast< _THelpee* > ( this )->mVal
通过?我认为,这与实例化THelper
时无法实例化TDerived
有关,但没有明确的理解。有人可以对这里发生的事情给出简短的解释和/或一些参考吗?谢谢你。
编辑:删除了“_T”前缀。
隐式实例化一个类并不会实例化其成员函数定义。 类模板的每个成员仅在使用时才会实例化(除非您使用显式实例化)。
您隐式实例化的第一件事是
TDerived<int>
,位于main
的第一行。 为了实例化它,编译器会查找 TDerived
模板,发现有几个依赖基类,因此尝试实例化它们。 TBase<int>
实例化没有问题。 但是在尝试实例化 THelper< TDerived<int>, TTraitsBase< TBase<int> > >
时,会尝试获取成员 TDerived<int>::T
,但 TDerived<int>
仍然是不完整的类型。
实例化
THelper< ... >
还注意到有一个成员函数int Foo()
,但不评估其定义的语义。
当您从
lObj.Foo()
调用 main
时,它会实例化 int THelper< ... >::Foo()
,TDerived<int>
是一个完整的类型,所以那里没有问题。
发布的代码有几个问题。由于我无法弄清楚其意图,我将列出我发现错误的内容。
(1) 使用不完整类型:
template< typename _T >
struct TDerived : TBase< _T > , THelper< TDerived< _T > , TTraitsBase< TBase< _T > > >
^^^^^^^^^^^^^^ it's incomplete
IMO 当你定义
TDerived<_T>
的主体时,你不应该使用相同的参数作为任何东西。
(2) 类型定义不当。
using TBase< _T >::T;
应该是,
typedef typename TBase< _T >::T T;
我想说你的 CRTP 中有一个循环依赖(正如其本质一样),但这意味着基模板类不允许使用其任何参数类,因为这非常参数类尚未定义 - 在实例化
Base<T>
时它仍然是一个不完整的类型。
所以,这样就可以了:
template <typename T> struct Base
{
T * impl; // incomplete type is OK
};
class Foo : public Base<Foo> { /* ... */ };
但这不是:
template <typename T> struct Base
{
T x; // need to know T, but T depends on Base<T>!
};