不明确的成员访问表达式:Clang拒绝有效代码吗?

Per*_*Per 23 c++ gcc clang language-lawyer name-lookup

我有一些代码,为了这个问题的目的,归结为

template<typename T>
class TemplateClass : public T {
 public:
  void method() {}
  template<typename U>
  static void static_method(U u) { u.TemplateClass::method(); }
};

class EmptyClass {};

int main() {
  TemplateClass<TemplateClass<EmptyClass> > c;
  TemplateClass<EmptyClass>::static_method(c);
}
Run Code Online (Sandbox Code Playgroud)

我试图用两个版本的两个编译器来编译它.GCC 4.2,4.4,4.6接受它而没有投诉.截至11月14日的Clang 2.9和SVN中继拒绝它,并显示以下错误消息:

example.cc:6:38: error: lookup of 'TemplateClass' in member access expression is
      ambiguous
  static void static_method(U u) { u.TemplateClass::method(); }
                                     ^
example.cc:13:3: note: in instantiation of function template specialization
      'TemplateClass<EmptyClass>::static_method<TemplateClass<TemplateClass<EmptyClass>
      > >' requested here
  TemplateClass<EmptyClass>::static_method(c);
  ^
example.cc:2:7: note: lookup in the object type
      'TemplateClass<TemplateClass<EmptyClass> >' refers here
class TemplateClass : public T {
      ^
example.cc:2:7: note: lookup from the current scope refers here
1 error generated.
Run Code Online (Sandbox Code Playgroud)

哪一个错了?我可以通过改变来解决Clang问题

  static void static_method(U u) { u.TemplateClass::method(); }
Run Code Online (Sandbox Code Playgroud)

  static void static_method(U u) { u.TemplateClass<T>::method(); }
Run Code Online (Sandbox Code Playgroud)

但是我希望能够理解何时可以忽略模板参数.


编辑:我曾经认为模糊性是在两个实例之间TemplateClass.下面的代码汇编了GCC和Clang,将这个假设称为疑问:

class E {};

template<typename T>
class A : public T {
 public:
  void method() {}
};

int main() {
  A<A<E> > a;
  a.A::method();
}
Run Code Online (Sandbox Code Playgroud)

sth*_*sth 11

我相信clang正确地拒绝了这段代码.

clang发现的歧义可以用一个不太复杂的例子来复制:

template<typename T>
class TemplateClass {
 public:
  void method() {}
  template<typename U>
  static void static_method(U u) { u.TemplateClass::method(); }                                  
};

struct A {};
struct B {};

int main() {
  TemplateClass<A> c;
  TemplateClass<B>::static_method(c);
}
Run Code Online (Sandbox Code Playgroud)

这里省略了模板中的继承,并且两个独立的类用于实例化.clang产生的错误保持不变.

首先,在TemplateClass<T>名称范围内TemplateClass指的是TemplateClass<T>,由于类名注入.这就是静态方法可以使用TemplateClass::method而不是更明确的原因TemplateClass<T>::method.

用于u.TemplateClass::method在静态方法中解释的名称查找在C++ 11和C++ 98标准的"3.4.5 Class member access [base.lookup.classref]"中定义.

相关部分是3.4.5/4:

如果类成员访问中的id-expression是表单的qualified-id

class-name-or-namespace-name::...
Run Code Online (Sandbox Code Playgroud)

[...]

这就是这种情况.该ID表达是部分的右侧.,并在我们的情况,这是合格的名称TemplateClass::method.

[...]
在整个postfix-expression的上下文和对象表达式的类的范围内查找or 运算符后面的class-name-or-namespace-name..->

"整个后缀表达式的范围 "是静态函数的主体,并且在这个静态函数TemplateClass中指的是TemplateClass<B>,因为函数是该类的一个成员(我们称之为TemplateClass<B>::static_method).

所以在这个范围内,名称是指TemplateClass<B>.

.在我们的例子中,"对象表达"是左边的部分c.类的c就是TemplateClass<A>在这个类的范围,TemplateClass是指TemplateClass<A>.

因此,根据查找使用的范围,名称指的是不同的实体.

该标准现在说:

如果在两个上下文中都找到该名称,则class-name-or-namespace-name应引用同一实体.

在我们的计划中并非如此.程序格式错误,编译器需要提供诊断消息.

模糊性保持如果您更换同BTemplateClass<A>,如使用的问题.


chi*_*ill 7

在ISO/IEC 14882:2011(E)中,"14.6.1本地声明的名称[temp.local]",[#5]说:

当使用模板的正常名称(即来自封闭范围的名称,而不是inject-class-name)时,它总是引用类模板本身而不是模板的特化.[示例:

template<class T> class X {
    X* p;    // meaning X<T>
    X<T>* p2;
    X<int>* p3;
    ::X* p4;    // error: missing template argument list
                // ::X does not refer to the injected-class-name
};
— end example ]
Run Code Online (Sandbox Code Playgroud)

这让我相信在你的例子u.TemplateClass::method();中相当于u.TemplateClass<T>::method();如果Clang在一个案例中给出错误并在另一个案例中干净地编译,那么这是一个Clang错误.