Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get an error when accessing a typedef in a derived class via CRTP?

Tags:

c++

crtp

I'm having trouble understanding why the code below doesn't compile -- could someone please explain?

How do I access a typedef in a derived class from the base class?

template<class Derived>
struct Test
{
    template<class T>
    typename Derived::value_type foo(T);
};

struct Derived : public Test<Derived>
{
    typedef int value_type;
};
like image 562
user541686 Avatar asked Dec 20 '22 17:12

user541686


2 Answers

At the time of declaring Derived, Derived is not yet a complete type -- you've only just started declaring it! Hence in the specialization Test<Derived>, the template argument is an incomplete type, and thus you mustn't refer to a nested name such as Derived::value_type -- that's circular logic.

You could decycle the problem by making the return type a separate argument:

template <typename T, typename R> struct Test
{
    template <typename U> R foo(U);
};

template <typename R>
struct BasicDerived : public Test<BasicDerived, R>
{
    typedef R value_type;
};

typedef BasicDerived<int> Derived;
like image 113
Kerrek SB Avatar answered Dec 24 '22 02:12

Kerrek SB


You can't access typedefs or members of the template class directly in the base class, because at that point it's not a complete type. Allowing this would lead to circular behaviour:

template<class Derived>
struct Test
{
    typedef typename Derived::value_type foo;
};

struct Derived : public Test<Derived>
{
    typedef Test<Derived>::foo value_type;
};

You can however reference members of the template class within methods, as they are not instantiated until later:

template<class Derived>
struct Test
{
    void foo() { typename Derived::value_type bar; }
};

struct Derived : public Test<Derived>
{
    typedef int value_type;
};

Alternatively, depending on what you are trying for, you can pass the typedef as an additional template parameter:

template<typename value_type, class Derived>
struct Test
{
    template<class T>
    value_type foo(T);
};

struct Derived : public Test<int, Derived>
{
    typedef int value_type;
};
like image 30
ecatmur Avatar answered Dec 24 '22 02:12

ecatmur