Я обнаружил, что при доступе к атрибуту non-template (v.foo
) из переменной типа шаблона (T& v
), С++ можно обмануть в мысли, что это шаблон-член, если есть шаблон функция с тем же именем (template class <T> void foo()
). Как это можно объяснить из спецификации С++? Рассмотрим эту простую программу:
#include <cassert>
/** Determine whether the 'foo' attribute of an object is negative. */
template <class T>
bool foo_negative(T& v)
{
return v.foo < 0;
}
struct X
{
int foo;
};
int main()
{
X x;
x.foo = 5;
assert(!foo_negative(x));
return 0;
}
У нас есть функция шаблона foo_negative
, которая принимает объект любого типа и определяет, является ли его атрибут foo отрицательным. Функция main
создает foo_negative
с помощью [T = X]. Эта программа компилируется и запускается без вывода.
Теперь добавьте эту функцию в начало программы:
template <class T>
void foo()
{
}
Компиляция с помощью g++ 4.6.3 приводит к ошибке этого компилятора:
funcs.cpp: In function ‘bool foo_negative(T&)’:
funcs.cpp:13:14: error: parse error in template argument list
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’:
funcs.cpp:25:5: instantiated from here
funcs.cpp:13:14: error: ‘foo’ is not a member template function
(где строка 13 - return v.foo < 0
, а строка 25 - assert(!foo_negative(x))
.)
Clang производит похожие ошибки.
Wat? Как добавление несвязанной функции, которая никогда не называется, позволяет ввести синтаксическую ошибку в действительную программу? При анализе foo_negative
компилятор не знает тип v
, и, самое главное, он не знает, является ли v.foo
шаблоном-членом или регулярным членом. По-видимому, он должен решить во время разбора (до создания шаблона), следует ли рассматривать его как шаблон-член или регулярный член.
Если он считает, что v.foo
является шаблоном-членом, то < 0
рассматривается как передача 0
в качестве аргумента шаблона, а отсутствует >
, следовательно, синтаксическая ошибка. Затем, когда foo_negative
создается с помощью [T = X], возникает еще одна ошибка, поскольку X::foo
не является шаблоном-членом.
Но почему он думает, что v.foo
является шаблоном-членом? Эта двусмысленность - это именно то, к чему относится ключевое слово template
: если я написал v.template foo
, тогда я бы прямо сказал С++ ожидать шаблон-член, но я не использовал ключевое слово template
! Я не ссылался на шаблон участника, поэтому он должен считать, что он является регулярным членом. Тот факт, что существует функция с тем же именем, что и член, не должен иметь никакого эффекта. Почему? Это не может быть ошибкой в компиляторе, потому что GCC и clang согласованы.