c++ - 不明确的成员访问表达式 : is Clang rejecting valid code?

标签 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);
}

我尝试使用两个编译器的多个版本来编译它。 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.

哪一个是错的?我可以通过更改来解决 Clang 问题

  static void static_method(U u) { u.TemplateClass::method(); }

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

但我希望自己能够理解何时可以省略模板参数。


编辑:我曾认为歧义存在于 TemplateClass 的两个实例之间。以下代码使用 GCC 和 Clang 编译,质疑该假设:

class E {};

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

int main() {
  A<A<E> > a;
  a.A::method();
}

最佳答案

我相信 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);
}

这里省略了模板中的继承,并使用了两个独立的类进行实例化。 clang 产生的错误还是一样。

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

用于解释 u.TemplateClass::method 的名称查找静态方法中的定义在 C++11 和 C++98 标准的“3.4.5 类成员访问[base.lookup.classref]”中。

相关部分是3.4.5/4:

If the id-expression in a class member access is a qualified-id of the form

class-name-or-namespace-name::...

[...]

这里就是这种情况。 id-expression. 右侧的部分在我们的例子中,这是限定名称 TemplateClass::method .

[...]
the class-name-or-namespace-name following the . or -> operator is looked up both in the context of the entire postfix-expression and in the scope of the class of the object expression.

“整个后缀表达式的作用域”是静态函数的主体,在这个静态函数中TemplateClassTemplateClass<B> ,因为该函数是该类的成员(我们称为 TemplateClass<B>::static_method )。

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

“对象表达式”是 . 的左边部分, 在我们的例子中 c . c的类(class)是 TemplateClass<A>在这个类的范围内,TemplateClassTemplateClass<A> .

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

标准现在说:

If the name is found in both contexts, the class-name-or-namespace-name shall refer to the same entity.

在我们的程序中并非如此。程序格式不正确,需要编译器给出诊断信息。

如果替换 B,歧义保持不变与 TemplateClass<A> ,如问题中所用。

关于c++ - 不明确的成员访问表达式 : is Clang rejecting valid code?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8100492/

相关文章:

c++ - 哪种方法可以在不阻塞主线程的情况下并行处理多个作业

c++ - 将 unsigned char * 转换为 hexstring

c++ - 如何实现排队 map ?

c++ - gcc 编译/链接后的 .h~ 文件是什么?

c++ - 没有将 bool 隐式转换为浮点类型的警告?

c++ - 将 `nullptr` 分配给 `bool` 类型。哪个编译器是正确的?

c++ - 包含 std::function 回调的多个包装器的 std::vector 不起作用

c++ - 从 constexpr 模板函数调用非 constexpr

c - 编译 CUDA 时出错

objective-c - clang 编译器从相同的来源生成不同的目标文件