typedef 上与 CRTP 相关的编译器错误

问题描述 投票:0回答:3

我想,关于 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”前缀。

c++ typedef crtp
3个回答
2
投票

隐式实例化一个类并不会实例化其成员函数定义。 类模板的每个成员仅在使用时才会实例化(除非您使用显式实例化)。

您隐式实例化的第一件事是

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>
是一个完整的类型,所以那里没有问题。


2
投票

发布的代码有几个问题。由于我无法弄清楚其意图,我将列出我发现错误的内容。

(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;

1
投票

我想说你的 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>!
};
© www.soinside.com 2019 - 2024. All rights reserved.