c++c++11crtptype-aliasincomplete-type

Is there a way for type aliases to depend on incomplete types in CRTP?


Take a simple CRTP example:

template <class Target>
struct StaticCallable
{
    static auto StaticCall()
    {
        return Target().Call();
    }

    using ReturnType = decltype(Target().Call());
};

struct StaticCallableInt : StaticCallable<StaticCallableInt>
{
    int Call() { return 2; }
};

int main()
{
    StaticCallableInt::ReturnType val;
    val = StaticCallableInt::StaticCall();
}

StaticCallable<> is instantiated with StaticCallableInt which at that point is not a complete type. For StaticCall(), that's okay. Evaluation is deferred until the function is called, and by then StaticCallableInt will be a complete type.

For the ReturnType alias, though, that's a problem, because it's evaluated on the spot, and it needs StaticCallableInt to have Call() declared by then. With this need unfulfilled, this code fails to compiles.

Are there any good techniques for type aliases in CRTP? A solution that pops out to me is to use a helper class for all the class details StaticCallable<> needs:

struct CallableInt
{
    int Call() { return 2; }
};

struct StaticCallableInt : StaticCallable<CallableInt> {};

Are there any other ways to have type aliases depend on incomplete types, possibly by deferring the alias's evaluation?


Solution

  • You can add another level of indirection by putting the alias in an internal struct.

    This will defer the evaluation of the alias:

    #include <iostream>
    
    template <class Target>
    struct StaticCallable
    {
        static auto StaticCall()
        {
            return Target().Call();
        }
        
        // Add this struct, with the type alias inside it:
        struct RetType
        {
            using value = decltype(Target().Call());
        };
    };
    
    struct StaticCallableInt : StaticCallable<StaticCallableInt>
    {
        int Call() { return 2; }
    };
    
    int main()
    {
        StaticCallableInt::RetType::value val;  // use the type alias
        val = StaticCallableInt::StaticCall();
        std::cout << val;
    }
    

    Output:

    2
    

    Live demo.