user965349
user965349

Reputation: 33

Issue refactoring curiously recurring template pattern

The following code fails to compile on g++ 4.6.1:

template<class Base>
struct GetBase {
  Base * getBase() {
    return static_cast<Base *>(this);
  }
};

template<class Derived>
struct Parent : private GetBase<Derived> {
  using GetBase<Derived>::getBase;
  int y() {
    return getBase()->x();
  }
};

struct Child : public Parent<Child> {
  int x() {
    return 5;
  }
  int z() {
    return y();
  }
};

with the error

In member function ‘Base* GetBase<Base>::getBase() [with Base = Child]’:
    instantiated from ‘int Parent<Derived>::y() [with Derived = Child]’
    instantiated from here
error: ‘GetBase<Child>’ is an inaccessible base of ‘Child’

Changing the static_cast to a reinterpret_cast will get the code to compile and it will work in this case, but I'm wondering if this is an acceptable solution in all cases? i.e., is it ever the case that a pointer to a base class is not the same as this? I'm assuming with multiple inheritance this may happen if the parents have data members? If GetBase is the first superclass, are the this pointers guaranteed to be equal?

Upvotes: 3

Views: 742

Answers (2)

Alexey Kukanov
Alexey Kukanov

Reputation: 12784

A good question; it caused me to learn something new about static_cast.

I think the following code achieves what you want : provides a base class for CRTP with a member function that casts this to the Derived type, yet only makes it accessible to the direct descendants of this base class. It compiles with GCC 4.3.4 (tested at ideone) and Clang (tested at llvm.org). Sorry, I couldn't resist to changing the names that I find confusing.

#include <iostream>

template<class Derived>
class CRTP {
protected:
  Derived * derived_this() {
    return static_cast<Derived *>(this);
  }
};

template<class Derived>
struct Parent : public CRTP<Derived> {
private:
  using CRTP<Derived>::derived_this;
public:
  int y() {
    return derived_this()->x();
  }
};

struct Child : public Parent<Child> {
  int x() {
    return 5;
  }
  int z() {
    return y();
  }
};

int main() {
  std::cout << Child().z() << std::endl;
  return 0;
}

This variant works because the inheritance is public, which enables the standard conversion of a pointer-to-derived-class to a pointer-to-base-class, and so also the inverse conversion (from base to derived) with static_cast, which CRTP needs. Private inheritance in your code prohibited this. So I made inheritance public, changed the method to be protected, and in Parent further restricted access by putting the using declaration into private section.

Upvotes: 1

Drew Dormann
Drew Dormann

Reputation: 63775

I'm wondering if this is an acceptable solution in all cases?

No. (See below)

is it ever the case that a pointer to a base class is not the same as this?

Yes.

  • With multiple inheritance, the base classes can not be expected to have the same address.

  • Depending on the compiler, a derived class with a vtable pointer may not have the same this as a base class that has no vtable pointer.

When explicitly upcasting to a base, static_cast is the appropriate C++ cast.

Upvotes: 3

Related Questions