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> который плохо сформирован. Поэтому вам нужно использовать квалификатор области :: с пробелом между < избегать орграфа.

Другие вопросы по тегам