模棱两可的成员模板查找

Ambiguous member template lookup

这个 question 的答案在下面的代码中说:

#include <vector>
using std::vector;

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

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

main 中的最后一行是有歧义的,因为编译器不仅在 foo 中查找 vector,而且还作为从 main 开始的非限定名称查找。所以它找到 std::vectorfoo::vector。要解决此问题,您必须编写

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

我已经在所有流行的 C++ 编译器(g++clang++vc++ 和 Intel C++)上尝试了这个程序,所有编译器都编译了这个程序,没有任何错误。那么,他为什么说这个节目有歧义呢? C++ 标准对此有何规定?

在 C++03 中是这种情况,但在 C++11 中已修复。我们甚至可以试试这个 live in godbolt with clang using -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 文档 using the same example from the defect report below 在描述 -Wambiguous-member-template 的警告时。

这是通过 defect report 1111: Remove dual-scope lookup of member template names 更改的,它解释了问题:

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.

这使得以下格式错误:

#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期 修复了析构函数名称但缺少成员的类似问题 函数。