Search code examples
c++c++11decltypecrtpstatic-cast

CRTP: How to infer type of member to be used as return type?


I would like to make the return type of a CRTP base method depend on the type of a member in the derived, as for example in:

template <typename C>
struct sum_a_b {
    ??? sum() { return static_cast<C*>(this)->a + static_cast<C*>(this)->b; }
}

template <typename T> struct a_b : sum_a_b<a_b<T>> { T a,b; };

What should I put in place of ???

I tried different ways to declare the return type :

template <typename T>
struct base {
    int get_ok() {  
        return static_cast<T*>(this)->value; 
    }
    auto get_invalid() -> decltype(static_cast<T*>(this)->value) {
        return static_cast<T*>(this)->value; 
    }
    typename T::value_type get_incomplete_type_foo() {  
        return static_cast<T*>(this)->value; 
    }
    auto get_incomplete_type_again() -> decltype(T().value) {  
        return static_cast<T*>(this)->value; 
    }
};

struct foo : base<foo> {
        typedef int value_type;
        value_type value;
};

The only methods that compiles is int get_ok, for the others I get either (for get_invalid_cast):

invalid static_cast from type 'base<foo>*' to type 'foo*'
     auto get_invalid() -> decltype(static_cast<T*>(this)->value) {  return static_cast<T*>(this)->value; }
                                    ^

or (the other two)

invalid use of incomplete type 'struct foo'
     typename T::value_type get_incomplete_type_foo() {  return static_cast<T*>(this)->value; }
                            ^

Solution

  • I think the only workaround available prior to c++14 is to use a type trait:

    #include <iostream>
    
    template<typename T>
    struct Value;
    
    template <typename T>
    struct Base
    {
        typename Value<T>::type get_value(void)
        {  
            return static_cast<T*>(this)->m_value; 
        }
    };
    
    struct Derived;
    
    template<> 
    struct Value<Derived>
    {
        using type = float;
    };
    
    struct Derived: public Base<Derived>
    {
        Value<Derived>::type m_value{};
    };
    
    int main()
    {
        Derived derived{};
        std::cout << derived.get_value() << std::endl;
    }
    

    online compiler

    If Derived type is a template then type trait specialization would look like this:

    template<typename U>
    struct Derived;
    
    template<typename U> 
    struct Value<Derived<U>>
    {
        using type = float;
    };