7

I try to implement a CRTP with templated class and I have an error with the following example code :

#include <iostream>

template<class T> class Traits
{
    public:
        typedef typename T::type type; // <- Error
                                       // : "no type named 'type' in 'class MyClass<double, 3u, 3u>'
        static const unsigned int m_const = T::m_const;
        static const unsigned int n_const = T::n_const;
        static const unsigned int size_const = T::m_const*T::n_const;
};

template<class T0> class Crtp
{
    public:
        typedef typename Traits<T0>::type crtp_type;
        static const unsigned int size = Traits<T0>::size_const; // <- This is OK
};

template<typename TYPE, unsigned int M, unsigned int N>
class MyClass : public Crtp< MyClass<TYPE, M, N> >
{
    public:
        typedef TYPE type;
        static const unsigned int m_const = M;
        static const unsigned int n_const = N;
};

int main()
{
    MyClass<double, 3, 3> x;
    std::cout<<x.size<<std::endl;
    return 0;
}

I do not understand what causes this problem nor how to fix it.

In fact my goal is that the CRTP class have to know the template arguments of the derived class WITHOUT passing them as template argument of the CRTP class.

Do you have any ideas how to implement this?

EDIT (relating to the first first) : My CRTP class has to be able to handle derived classes with different number of template parameters

Vincent
  • 57,703
  • 61
  • 205
  • 388

1 Answers1

8

The problem is that MyClass is incomplete in its own base class list, where you instantiate Crtp<MyClass>. But instantiating Crtp<MyClass<...>> requires instantiating Traits<MyClass<...>> which then requires instantiating MyClass<...>::type which is impossible because it's incomplete. You have a circular dependency.

In fact my goal is that the CRTP class have to know the template arguments of the derived class

That can be done using a partial specialzation and a template-template-parameter, like so:

#include <iostream>

template<typename T> class Crtp;

template<template<typename, unsigned, unsigned> class T, typename U, unsigned M, unsigned N>
class Crtp< T<U, M, N> >
{
    public:
        typedef U crtp_type;
        static const unsigned int size = M * N;
};

template<typename TYPE, unsigned int M, unsigned int N>
class MyClass : public Crtp< MyClass<TYPE, M, N> >
{
};

int main()
{
    MyClass<double, 3, 3> x;
    std::cout<<x.size<<std::endl;
    return 0;
}
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • It would be nice if you could explain how partial specialization solves the issue? – Mr.Anubis Aug 03 '12 at 13:20
  • I edited my post because ideally my crtp class should be able to handle derived classes with different number of parameters. – Vincent Aug 03 '12 at 13:27
  • 1
    @Mr.Anubis, it avoids the circular dependency, instead a partial specialization matches the form of the template and "picks apart" `MyClass` into the template `MyClass` and its arguments `T`, `M` and `N`, so ti can then refer to those arguments. – Jonathan Wakely Aug 03 '12 at 14:34
  • @Vincent, that could be done with aditional specializations. Maybe not ideal, but it has the advantage that it compiles and works, which your original didn't ;) – Jonathan Wakely Aug 03 '12 at 14:35
  • @JonathanWakely arhhh I just noticed you removed traits class and doing the thing directly :) – Mr.Anubis Aug 03 '12 at 14:48
  • If it's a circular dependency, why `Crtp::size` works? – Mikhail Mar 12 '21 at 16:18