1
votes

Going from a basic CRTP with the core concept being the this pointer cast -

#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;
}

why does the cast fail if A is no template? (MSVC: error C2440: 'static_cast' : cannot convert from 'A *const ' to 'B *')

Or the other way around, why does it work if it is a template.

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

Probably to do with the timing of template instantiation, but I'm curious about the details.

2

2 Answers

0
votes

It is not a problem of template instantiation but a problem a order of definition.

While declaring class A, class B is still unknown and as such, you cannot ask the compiler to validate a static_cast or a dynamic_cast. Only a reinterpret_cast or C style cast would be valid at that point.

But is is easy to fix : just reject the definition of a0 after the declaration of B :

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

That way, B has been declared and the static_cast is accepted.

0
votes
  • The cast fails if A isn't a template because static_cast allows several cast types, and since it doesn't know the relationship between A and B it fails the cast.
  • The cast succeeds in the template case only because of a bug in MSVC. Since B is a non-dependent type name it should behave the same as the non-template case. However MSVC improperly holds back the check until the point of instantiation at which type it knows the relationship between A and B. (I tried to compiler on g++ and it did fail as expected).

Additionally note that what you've done isn't CRTP at all. In a normal CRTP you would want to cast to T* not B* which should then compile just fine. I'm also assuming you meant class B: public A<B> not class B: public A<int>.