Непонятная ошибка шаблона - PullRequest
75 голосов
/ 24 сентября 2010

Я играл с clang некоторое время и наткнулся на "test / SemaTemplate / зависимый-шаблон-recovery.cpp" (в дистрибутиве clang), который должен предоставить подсказки для восстановления после ошибки шаблона.

Все это можно легко сократить до минимального примера:

template<typename T, typename U, int N> struct X {
    void f(T* t)
    {
        // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
        t->f0<U>();
    }
};

Сообщение об ошибке от clang:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
         t->f0<U>();
            ^
            template 
1 error generated.

... Но мне трудно понять, куда именно нужно вставить ключевое слово template, чтобы иметь синтаксически правильный код?

Ответы [ 4 ]

78 голосов
/ 24 сентября 2010

ISO C ++ 03 14.2 / 4:

Когда имя специализации шаблона члена появляется после.или -> в выражении postfix, или после спецификатора вложенного имени в квалифицированном идентификаторе, а выражение postfix или уточненный идентификатор явно зависит от параметра-шаблона (14.6.2), шаблон элементаимя должно начинаться с ключевого слова template .В противном случае предполагается, что имя не является шаблоном.

In t->f0<U>(); f0<U> - это специализация шаблона члена, которая появляется после -> и которая явно зависит от параметра шаблона U,поэтому специализация шаблона элемента должна начинаться с ключевого слова template.

Поэтому измените t->f0<U>() на t->template f0<U>().

22 голосов
/ 24 сентября 2010

В дополнение к замечаниям других, обратите внимание, что иногда компилятор не может решить, и обе интерпретации могут дать альтернативные действительные программы при создании экземпляра

#include <iostream>

template<typename T>
struct A {
  typedef int R();

  template<typename U>
  static U *f(int) { 
    return 0; 
  }

  static int f() { 
    return 0;
  }
};

template<typename T>
bool g() {
  A<T> a;
  return !(typename A<T>::R*)a.f<int()>(0);
}


int main() {
  std::cout << g<void>() << std::endl;
}

Печатается 0 при пропуске template перед f<int()>, но 1 при его вставке. Я оставляю это как упражнение, чтобы выяснить, что делает код.

9 голосов
/ 24 сентября 2010

Вставьте его непосредственно перед точкой, где указывается каретка:

template<typename T, typename U, int N> struct X {
     void f(T* t)
     {
        t->template f0<U>();
     }
};

Правка: причина этого правила становится понятнее, если вы думаете как компилятор. Как правило, компиляторы смотрят вперед только на один или два токена и обычно не «смотрят вперед» на остальную часть выражения. [Редактировать: см. Комментарий] Причина для ключевого слова та же, что и длявам нужно ключевое слово typename для обозначения имен зависимых типов: оно говорит компилятору: «Эй, идентификатор, который вы увидите, - это имя шаблона, а не имя статического члена данных, за которым следует меньше чем»знак».

7 голосов
/ 24 сентября 2010

выдержка из C ++ шаблонов

Конструкция .template Очень похожая проблема была обнаружена после введения typename. Рассмотрим следующий пример с использованием стандартного типа набора битов:

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
                                       allocator<char> >(); 
} 

Странная конструкция в этом примере - .template. Без такого дополнительного использования шаблона компилятор не знает, что следующий за ним токен «<» не является «меньше чем», но является началом списка аргументов шаблона. Обратите внимание, что это проблема, только если конструкция перед периодом зависит от параметра шаблона. В нашем примере параметр bs зависит от параметра шаблона N. </p>

В заключение, обозначение .template (и аналогичные обозначения, такие как -> template) следует использовать только внутри шаблонов и только в том случае, если они соответствуют чему-то, что зависит от параметра шаблона.

...