Черты CRTP, работающие только с шаблонным производным классом - PullRequest
0 голосов
/ 20 марта 2019

Я видел идиому для использования признаков производного типа в базовом классе шаблона CRTP, который выглядит следующим образом:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

template <typename T>
struct Derived1 : Base<Derived1<T>>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};

template <typename T>
struct traits<Derived1<T>> {
    using size_type = size_t;
};

int main()
{
    using T = float;
    Derived1<T> d1;
    d1.print();
}

Насколько я понимаю, цель этой идиомы состоит в том, чтобы отложить создание экземпляраBase класса size_type.Что меня смущает, так это то, что этот шаблон работает только в том случае, если производный класс сам шаблонизирован.Например, если мы изменим код на:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

struct Derived1 : Base<Derived1>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};

template <>
struct traits<Derived1> {
    using size_type = size_t;
};

int main()
{
    Derived1 d1;
    d1.print();
}

, то получим ошибку

prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19:   required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
     using size_type = typename traits<Derived>::size_type;
                                                          ^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
 struct traits;
        ^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
         Derived1<float> d1;

Может ли кто-нибудь дать мне объяснение, указывающее, почему шаблонный производный класс компилируется, но не шаблонныйкласс не?

Ответы [ 2 ]

1 голос
/ 20 марта 2019

Проблема, которую вы видите, не имеет ничего общего с CRTP.

Вот что упоминает стандарт.

Если шаблон класса был объявлен, но не определен в момент его создания (13.7.4.1), инстанцирование дает неполный тип класса (6.7). [Пример:

template<class T> class X; X<char> ch; // error: incomplete type
X<char>

Ваш traits был объявлен только в момент создания экземпляра Base<Derived>, следовательно, согласно стандарту ( см. Извлечение из стандарта ), struct traits<Derived> дает неполный тип.

Вам следует изменить порядок кода, чтобы он видел специализацию traits<Derived>, когда создается экземпляр Base<Derived>.

1 голос
/ 20 марта 2019

Ошибка компиляции, которую вы видите, не имеет ничего общего с CRTP, это всего лишь небольшая путаница зависимостей.

В коде без шаблона вашей структуре "Base" требуется определение специализированной структуры "traits", но она появляется только потом, поэтому она пытается использовать неполный тип, который видел в объявлении выше.

Чтобы код работал, вам нужно иметь специализацию "traits" перед объявлением Base, которое требует, чтобы вы также добавили объявление Derived 1, вот код компиляции:

class Derived1;

template<typename Derived>
struct traits;

template <>
struct traits<Derived1> {
    using size_type = size_t;
};

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

struct Derived1 : Base<Derived1>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};


int main()
{
    Derived1 d1;
    d1.print();
}
...