Подтвердить что ты не робот

Сложное имя атрибута С++ для шаблона участника

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

4b9b3361

Ответ 1

В Clang это было PR11856, которое было исправлено ~ 2,5 месяца назад. Clang trunk и Clang 3.1 не сообщают никаких ошибок с этим кодом. Ошибка Clang включает пояснение о том, почему этот код был отклонен, и почему код верен, воспроизведен здесь (слегка измененный для решения вашего дела)

Этот параграф имеет значение [basic.lookup.classref] p1:

"В выражении доступа к члену класса (5.2.5), если токен . или ->сразу же следует идентификатор, за которым следует <, идентификатор должны быть проверены, чтобы определить, является ли < началом список аргументов шаблона (14.2) или меньше, чем оператор. Идентификатор сначала просматривается в классе выражения объекта. Если идентификатор не найден, он затем просматривается в контексте целое постфиксное выражение и назовем шаблон класса."

Так как v зависит, по-видимому, идентификатор не найден, поэтому мы подумайте, что произойдет, если мы посмотрим в контексте всего постфикс-выражение. Поскольку мы находим шаблон функции, мы не должны заключаем, что у нас есть начало идентификатора шаблона.

Ответ 2

Это похоже на ошибку компилятора.

В стандарте говорится:

После поиска имени (3.4) обнаружено, что имя является именем шаблона или идентификатор оператора-функции или идентификатор литерала-оператора ссылается на набор перегруженные функции, любой из которых является шаблоном функции, если за этим следует <, < всегда принимается за разделитель template-argument-list и никогда не меньше, чем оператор.

и в 3.4.5/1

В выражении доступа к члену класса (5.2.5), если. или → токен после чего следует идентификатор, за которым следует идентификатор <, идентификатор должны быть проверены, чтобы определить, является началом список аргументов шаблона (14.2) или меньше, чем оператор. Идентификатор сначала просматривается в классе выражения объекта. Если идентификатор не найден, он затем просматривается в контексте всего постфиксного выражения и назовем шаблон класса.

Стандарт, похоже, не указывает, что поиск имени может когда-либо найти шаблон функции, не являющийся членом. В любом случае значение < должно определяться во время определения шаблона, а не времени создания (это слишком поздно).

Ответ 3

Это ошибка.

В MSVC (2011) ваш код работает нормально. Я думаю, что синтаксический анализатор компилятора перевел '<' как начальный токен для оператора шаблона. Но почему у Clang и GCC есть эта ошибка в то время?

Сообщите об ошибке здесь и здесь.

Возможно, это тоже интересно: Еще одна ошибка в g++/Clang? [Шаблоны С++ интересны]