Ошибка при запуске шаблона

Я играл с clang некоторое время, и я наткнулся на "test/SemaTemplate/dependent-template-recover.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, чтобы код был синтаксически правильным?

Ответ 1

ISO С++ 03 14.2/4:

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

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

Итак, измените t->f0<U>() на t->template f0<U>().

Ответ 2

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

#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 при вставке. Я оставляю это как упражнение, чтобы выяснить, что делает код.

Ответ 3

Вставьте его непосредственно перед точкой, где находится каретка:

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

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

Ответ 4

Выдержка из С++ Templates

Конструкция .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.

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