Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous member template lookup

An answer to this question says in the following code:

#include <vector>
using std::vector;

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

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

The last line in main is ambiguous, because the compiler not only looks up vector within foo, but also as an unqualified name starting from within main. So it finds both std::vector and foo::vector. To fix this, you have to write

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

I've tried this program on all popular C++ compilers ( g++, clang++, vc++ and Intel C++ ) and all compilers compile this program without any error. So, why did he say that there is ambiguity in this program ? What does the C++ standard say about this ?

like image 565
Destructor Avatar asked Dec 23 '22 03:12

Destructor


1 Answers

This was the case in C++03 but this was fixed in C++11. We can even try this live in godbolt with clang using -std=c++03 flag. We do receive a warning:

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

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

Older clang documentation using the same example from the defect report below when describing the warning for -Wambiguous-member-template.

This was changed via defect report 1111: Remove dual-scope lookup of member template names which explains the issue:

According to 6.4.5 [basic.lookup.classref] paragraph 1,

In a class member access expression (8.2.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (17.2 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. 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 and

  • if the name is not found, the name found in the class of the object expression is used, otherwise

  • if the name is found in the context of the entire postfix-expression and does not name a class template, the name found in the class of the object expression is used, otherwise

  • if the name found is a class template, it shall refer to the same entity as the one found in the class of the object expression, otherwise the program is ill-formed.

This makes the following ill-formed:

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

That's confusing and unnecessary. The compiler has already done the lookup in X's scope, and the obviously-correct resolution is that one, not the identifier from the postfix-expression's scope. Issue 305 fixed a similar issue for destructor names but missed member functions.

like image 193
Shafik Yaghmour Avatar answered Jan 05 '23 23:01

Shafik Yaghmour