Подтвердить что ты не робот

Clang++ не принимает использование параметра шаблона шаблона при использовании CRTP

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

Проблема может быть реплицирована с помощью этого фрагмента кода:

template <template<class> class Derived, class T>
struct base
{
};

template <class T>
struct derived : public base<derived, T>
{
    derived()
        : base<derived, T>()
    { }
};

Нарушение сообщения об ошибке:

bug.cpp:10:16: error: template argument for template template parameter must be a class template or type alias template
        : base<derived, T>()
               ^
bug.cpp:10:11: error: expected class member or base class name
        : base<derived, T>()
          ^
bug.cpp:10:11: error: expected '{' or ','
3 errors generated.

Эта проблема возникает только на clang (3.4), а не g++ (4.8, 4.7, 4.6). Я также компилирую с -std = С++ 11.

Это первый раз, когда мне нужно использовать CRTP с параметром шаблона шаблона. Я делаю это хорошо, и проблема с clang++ или нет?

Я вырос, чтобы доверять сообщениям clang++ об ошибках больше, чем g++ в последнее время!

4b9b3361

Ответ 1

Ваш код является законным.

Из стандарта С++ 11, раздел 14.6.1:

Подобно обычным (не шаблонным) классам, шаблоны классов имеют имя с введенным классом (раздел 9). Имя введенного класса может использоваться как имя-шаблона или имя типа. Когда он используется с шаблоном-аргументом-списком, в качестве аргумента-шаблона для шаблона-шаблона шаблона или в качестве окончательного идентификатора в специфицированном типе-спецификаторе объявления класса класса друга, относится к самому шаблону класса.

Похоже, что ваша версия clang все еще реализует старое правило. Исходя из ваших дополнительных комментариев, это может быть сделано только в списке инициализаторов ctor.


Пользователь Дэвид Родригес - dribeas предоставил обходной путь для компиляторов, которые не полностью реализовали правило С++ 11 с введенным классом. Используйте любое имя класса, который не является неквалифицированным, например:

derived()
    : base< ::derived, T >()
//          ^^ qualified with global namespace
{ }

Некоторые компиляторы могут также требовать этого в списке наследования:

template <class T>
struct derived : public base< ::derived, T >
//                            ^^