CRTP铸造细节

时间:2014-12-10 18:19:16

标签: c++ templates crtp

从基本的CRTP开始,核心概念是这个指针演员 -

#include "stdafx.h"
#include <iostream>

using namespace std;

template<class T>
class A
{
 public:

   void a0(){ static_cast<B*>(this)->a2();  }
   void a2(){ cout << "a2 base" << endl; }
};

class B: public A<int>//<int>
{
 public:
   void a1(){ a0(); }
   void a2(){ cout << "a2 derived" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{

   B b;
   b.a1();
   return 0;
}

如果A不是模板,为什么演员会失败? (MSVC:错误C2440:'static_cast':无法从'A * const'转换为'B *')

或者反过来说,如果模板,为什么会这样呢?

class B;
class A
{
 public:

   void a0(){ static_cast<B*>(this)->a2(); }
   void a2(){ cout << "a2 base" << endl; }
};

class B: public A
{
 public:

   void a1(){ a0(); }
   void a2(){ cout << "a2 derived" << endl; }
};

可能与模板实例化的时间有关,但我对细节感到好奇。

2 个答案:

答案 0 :(得分:0)

  • 如果A不是模板,则投射失败,因为static_cast允许多种投射类型,并且因为它不知道A和{{1}之间的关系它失败了演员。
  • 由于MSVC中的错误,演员在模板案例中成功。由于B是非依赖类型名称,因此其行为应与非模板案例相同。但是,MSVC不正确地阻止检查,直到实例化它知道BA之间的关系的类型。 (我尝试在g ++上编译,但确实按预期失败了。)

另外请注意,您所做的并不是CRTP。在普通的CRTP中,您希望转换为B而不是T*,然后编译就好了。我还假设您的意思是B*而不是class B: public A<B>

答案 1 :(得分:0)

这不是模板实例化的问题,而是一个定义顺序的问题。

在声明A类时,B类仍然是未知的,因此,您不能要求编译器验证static_cast或dynamic_cast。此时只有reinterpret_cast或C样式转换才有效。

但很容易修复:在声明B之后拒绝a0定义

class B;
class A
{
 public:

   void a0();
   void a2(){ cout << "a2 base" << endl; }
};

class B: public A
{
 public:
   void a1(){ a0(); }
   void a2(){ cout << "a2 derived" << endl; }
};

void A::a0() {
    static_cast<B*>(this)->a2();
}

这样,B已被声明并且static_cast被接受。