Clang и Intel не могут скомпилировать этот код CRTP
Я написал небольшую библиотеку, которая использует множество методов метапрограммирования C++11 и CRTP, и она хорошо компилируется с g++ 4.7.2
Теперь я пытаюсь скомпилировать его с Intel ICPC 13.0.0.079, и он генерирует несколько сотен ошибок. Поэтому я стараюсь выявлять проблемы одну за другой.
Итак, во-первых, рассмотрим этот код, который без проблем компилируется под g++ 4.7.2
#include <iostream>
template<template<typename> class Crtp, typename Type>
struct Base {};
template<typename Type>
struct Derived : public Base<Derived, Type>
{
Derived(): Base<Derived, Type>() {;}
};
int main()
{
Derived<int> x;
return 0;
}
И icpc, и clang не могут скомпилировать этот код:
test_crtp.cpp(26): error: type "Derived<Type>::Derived" is not a class template
Derived(): Base<Derived, Type>() {;}
^
test_crtp.cpp(26): error: "Base" is not a nonstatic data member or base class of class "Derived<int>"
Derived(): Base<Derived, Type>() {;}
^
detected during instantiation of "Derived<Type>::Derived() [with Type=int]" at line 31
compilation aborted for test_crtp.cpp (code 2)
Так это ошибка в intel и clang, или в g++? Если это в intel и clang, думаете ли вы, что это будет решено в будущей версии?
2 ответа
Внутри класса Derived
, имя Derived
ссылается на (созданный экземпляр) класс, а не на шаблон класса. Пытаться Base< ::Derived, Type>
вместо этого (будьте осторожны, чтобы оставить пробел между <и::).
В разделе 9.2.3 шаблона C++ "Полное руководство" ( Amazon) обсуждается "Имена введенных классов". Цитировать:
Шаблоны классов также имеют введенные имена классов. Однако они более странные, чем обычные внедренные имена классов: за ними могут следовать аргументы шаблона (в этом случае им присваиваются имена шаблонов классов), но если за ними не следуют аргументы шаблона, они представляют класс с его параметрами в качестве аргументов. (или, для частичной специализации, его аргументы специализации). Это объясняет следующую ситуацию:
template<template<typename> class TT>
class X {};
template<typename T>
class C
{
Ca; // OK: same as ''C<T> a;''
C<void> b; // OK
X<C> c; // ERROR: C without a template argument list
// does not denote a template
X<::C> d; // ERROR: <: is an alternative token for [
X< ::C> e; // OK: the space between < and :: is required
}
Обратите внимание, что неквалифицированное имя относится к введенному имени и не считается именем шаблона, если за ним не следует список аргументов шаблона. Чтобы компенсировать это, мы можем принудительно найти имя шаблона, используя спецификатор области видимости::. Это работает, но мы должны быть осторожны, чтобы не создавать так называемый орграф токен<:, который интерпретируется как левая скобка. Хотя такие ошибки встречаются относительно редко, они приводят к недоумению в диагностике.
Итак, что происходит в вашем коде, так это Base<Derived, Type>
интерпретируется как Base<Derived<Type>, Type>
который плохо сформирован. Поэтому вам нужно использовать квалификатор области ::
с пробелом между <
избегать орграфа.