Неоднозначный поиск шаблона элемента

Ответ на этот вопрос содержится в следующем коде:

#include <vector>
using std::vector;

struct foo {
  template<typename U>
  void vector();
};

int main() {
  foo f;
  f.vector<int>(); // ambiguous!
}

Последняя строка в main неоднозначна, потому что компилятор ищет не только vector в foo, но также как неквалифицированное имя, начиная с main. Таким образом, он находит и std::vector, и foo::vector. Чтобы исправить это, вы должны написать

f.foo::vector<int>();

Я пробовал эту программу на всех популярных компиляторах C ++ (g++, clang++, vc++ и Intel C ++), и все компиляторы компилируют эту программу без ошибок. Итак, почему он сказал, что в этой программе есть двусмысленность? Что об этом говорит стандарт C ++?


person Destructor    schedule 04.10.2018    source источник
comment
Это очень старый ответ. С тех пор компиляторы (и сам язык) эволюционировали.   -  person 1201ProgramAlarm    schedule 04.10.2018
comment
@ 1201ProgramAlarm: Если компиляторы и сам язык эволюционировали, я хотел бы знать старые и новые правила, которые вызывают это изменение.   -  person Destructor    schedule 04.10.2018


Ответы (1)


Так было в C ++ 03, но это было исправлено в C ++ 11. Мы даже можем попробовать этот вживую в Godbolt с clang, используя -std = c ++ 03 flag. Мы получаем предупреждение:

<source>:11:5: warning: lookup of 'vector' in member access expression is ambiguous; using member of 'foo' [-Wambiguous-member-template]

  f.vector<int>(); // ambiguous!
    ^

Старая документация clang с использованием того же пример из отчета о дефектах ниже при описании предупреждения для -Wambiguous-member-template.

Это было изменено с помощью отчета о дефектах 1111: Удалить двойную область действия поиск имен шаблонов элементов, который объясняет проблему:

Согласно пункту 1 раздела 6.4.5 [basic.lookup.classref],

В выражении доступа к члену класса (8.2.5 [expr.ref]), если. или -> token сразу же сопровождается идентификатором, за которым следует ‹, идентификатор должен быть найден, чтобы определить, является ли‹ началом списка аргументов шаблона (17.2 [temp.names]) или оператором «меньше». Идентификатор сначала ищется в классе объектного выражения. Если идентификатор не найден, он затем ищется в контексте всего постфиксного выражения и должен назвать шаблон класса. Если поиск в классе объектного выражения находит шаблон, имя также ищется в контексте всего постфиксного выражения и

  • если имя не найдено, используется имя, найденное в классе выражения объекта, в противном случае

  • если имя найдено в контексте всего постфиксного выражения и не называет шаблон класса, используется имя, найденное в классе выражения объекта, в противном случае

  • если найденное имя является шаблоном класса, оно должно ссылаться на ту же сущность, что и найденная в классе объектного выражения, в противном случае программа имеет неправильный формат.

Это делает неправильно сформированным следующее:

#include <set>
using std::set;
struct X {
  template <typename T> void set(const T& value);
};
void foo() {
  X x;
  x.set<double>(3.2);
}

Это сбивает с толку и ненужно. Компилятор уже выполнил поиск в области X, и очевидно правильное разрешение - это разрешение, а не идентификатор из области видимости постфиксного выражения. В выпуске 305 устранена аналогичная проблема для имен деструкторов, но отсутствуют функции-члены.

person Shafik Yaghmour    schedule 04.10.2018
comment
Что это значит, когда говорится, что не идентификатор из области видимости постфиксного выражения? - person Destructor; 04.10.2018
comment
@Destructor IIUC имеет в виду If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression - person Shafik Yaghmour; 04.10.2018