Ошибка при запуске шаблона
Я играл с 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) должна использоваться только внутри шаблонов и только если они следуют за тем, что зависит от параметра шаблона.