How to write curiously recurring templates with more than 2 layers of inheritance?

I'm not entirely clear on what you're hoping to accomplish, but this is a close approximation of what you seem to be asking for.

template <typename LowestDerivedClass> class A {
public:
  LowestDerivedClass &get() {
    return *static_cast<LowestDerivedClass *>(this); 
  }
  void print() {
    cout << "A"; 
  }
};

template <typename LowestDerivedClass>
class Bbase : public A<LowestDerivedClass> {
public:
  void print() {
    cout << "B";
    this->A<LowestDerivedClass>::print();
  }
};

class B : public Bbase<B> {};

class C : public Bbase<C> {
public:
  void print() {
    cout << "C";
    this->Bbase<C>::print();
  }
};

int main() {
  C c;
  c.print();
  cout << endl;
  B b;
  b.print();
  cout << endl;
}

I changed the output to illustrate the inheritance better. In your original code, you can't pretend B isn't a template [the best you could hope for is B<>], so something like this is probably the least kludgy way of handling it.


From your other answer, (2) is not possible. You can leave off template parameters for functions, if the function's arguments are sufficient to infer them, but with classes you have to provide something. (1) can be done, but its awkward. Leaving off all the various layers:

template<typename T> struct DefaultTag { typedef T type; };
template<typename Derived = void>
class B : public A<Derived> { /* what B should do when inherited from */ };
template<>
class B<void> : public A<DefaultTag<B<void> > > { /* what B should do otherwise */ };

You have to do something similar at each level. Like I said, awkward. You can't simply say typename Derived = DefaultTag<B> > or something similar because B doesn't exist yet.


Here is what I've settled on, using a variation on CRTP's to solve the problem presented in my motivation example. Probably best read starting at the bottom and scrolling up..

#include "boost/smart_ptr.hpp"
using namespace boost;

// *** First, the groundwork....
//     throw this code in a deep, dark place and never look at it again
//
//     (scroll down for usage example)

#define DefineBuilder(TYPE, BASE_TYPE) \
    template<typename TargetType, typename ReturnType> \
    class TemplatedBuilder<TYPE, TargetType, ReturnType> : public TemplatedBuilder<BASE_TYPE, TargetType, ReturnType> \
    { \
    protected: \
        TemplatedBuilder() {} \
    public: \
        Returns<ReturnType>::me; \
        Builds<TargetType>::options; \

template<typename TargetType>
class Builds
{
public:
    shared_ptr<TargetType> create() {
        shared_ptr<TargetType> target(new TargetType(options));
        return target;
    }

protected:
    Builds() {}
    typename TargetType::Options options;
};

template<typename ReturnType>
class Returns
{
protected:
    Returns() {}
    ReturnType& me() { return *static_cast<ReturnType*>(this); }
};

template<typename Tag, typename TargetType, typename ReturnType> class TemplatedBuilder;
template<typename TargetType> class Builder : public TemplatedBuilder<TargetType, TargetType, Builder<TargetType> > {};

struct InheritsNothing {};
template<typename TargetType, typename ReturnType>
class TemplatedBuilder<InheritsNothing, TargetType, ReturnType> : public Builds<TargetType>, public Returns<ReturnType>
{
protected:
    TemplatedBuilder() {}
};

// *** preparation for multiple layer CRTP example *** //
//     (keep scrolling...)

class A            
{ 
public: 
    struct Options { int a1; char a2; }; 

protected:
    A(Options& o) : a1(o.a1), a2(o.a2) {}
    friend class Builds<A>;

    int a1; char a2; 
};

class B : public A 
{ 
public: 
    struct Options : public A::Options { int b1; char b2; }; 

protected:
    B(Options& o) : A(o), b1(o.b1), b2(o.b2) {}
    friend class Builds<B>;

    int b1; char b2; 
};

class C : public B 
{ 

public: 
    struct Options : public B::Options { int c1; char c2; };

private:
    C(Options& o) : B(o), c1(o.c1), c2(o.c2) {}
    friend class Builds<C>;

    int c1; char c2; 
};


// *** many layer CRTP example *** //

DefineBuilder(A, InheritsNothing)
    ReturnType& a1(int i) { options.a1 = i; return me(); }
    ReturnType& a2(char c) { options.a2 = c; return me(); }
};

DefineBuilder(B, A)
    ReturnType& b1(int i) { options.b1 = i; return me(); }
    ReturnType& b2(char c) { options.b2 = c; return me(); }
};

DefineBuilder(C, B)
    ReturnType& c1(int i) { options.c1 = i; return me(); }
    ReturnType& c2(char c) { options.c2 = c; return me(); }
};

// note that I could go on forever like this, 
// i.e. with DefineBuilder(D, C), and so on.
//
// ReturnType will always be the first parameter passed to DefineBuilder.
// ie, in 'DefineBuilder(C, B)', ReturnType will be C.

// *** and finally, using many layer CRTP builders to construct objects ***/

int main()
{
    shared_ptr<A> a = Builder<A>().a1(1).a2('x').create();
    shared_ptr<B> b = Builder<B>().a1(1).b1(2).a2('x').b2('y').create();
    shared_ptr<B> c = Builder<C>().c2('z').a1(1).b1(2).a2('x').c1(3).b2('y').create(); 
    // (note: any order works)

    return 0;
};