Search code examples
c++templatessubclassingcrtp

Curiously Recurring Template and Template parameter dependent subclassing issues


I am trying to make the following code work

template < class __derived, class __object = typename __derived::Object >
struct Base {
    using Derived = __derived;
    using Object = __object;
    void function(Object o) { return Derived::function(s); }
}

//template < class __derived >
//struct Base {
//    using Derived = __derived;
//    using Object = typename Derived::Object;
//    void function(Object o) { return Derived::function(s); }
//}

template < class __object >
struct Derived : public Base< Derived< __Object > > {
    using Object = __object;
    void function(Object o) { ... }
}

And i instantiate an object by declaring

Derived<double> obj;

Problem is, the compiler claims not to be able to find the symbol Object inside the Derived class while deducing the second template parameter for the Base class. The same error is also generated by the commented version.

I am trying to do this under inspiration of Eigen3 code, particularly the CRTP (Curiously Recurring Template Pattern) they use in order to avoid use of virtual functions. Eigen3 actually uses a traits class but I can't figure out how to imitate that for the present case. Anyone has any suggestion about this? Thanks in advance!


Solution

  • Normally, if you want A to inherit from B, then B cannot know anything about A other than it's declaration:

    template < class __object >
    struct Derived;
    

    Unfortunately, you want to get more, so you'll have to use a type trait:

    template<class __derived>
    struct Base_traits {
        //using Object = ?????;
    };
    template<class __object>
    struct Base_traits<Derived<__object>> {
        using Object = __object; //note, this also can't inspect B.
    };
    

    The Base class can inspect the Base_traits all it wants, because the traits don't inspect B at all.

    template < class __derived, class __object = typename Base_traits<__derived>::Object >
    struct Base {
        using Derived = __derived;
        using Object = typename Base_traits<__derived>::Object;
        //or        
        using Object = __object;
    


    Unrelated, leading double underscores are disallowed for use by mortals, use a single leading underscore followed by a lowercase letter. Alternatively, use a trailing underscore.

    Also, the syntax

    void function(Object o) { return Derived::function(s); }
    

    Won't work, because that notation cannot be used for upcasts, only downcasts. Ergo, you have to use static_cast on the this. Since that's vaguely ugly, I put it behind a function:

        void foo(Object o) { self()->bar(o); }
    private:
        __derived* self() {return static_cast<__derived*>(this);}
        const __derived* self() const {return static_cast<__derived*>(this);}
    };
    

    Full code: http://coliru.stacked-crooked.com/a/81595b0fcd36ab93