我有一些代码,就这个问题而言,归结为
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>
的范围内姓名TemplateClass
指TemplateClass<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.
“整个后缀表达式的作用域”是静态函数的主体,在这个静态函数中TemplateClass
指TemplateClass<B>
,因为该函数是该类的成员(我们称为 TemplateClass<B>::static_method
)。
所以在这个范围内,名称指的是 TemplateClass<B>
.
“对象表达式”是 .
的左边部分, 在我们的例子中 c
. c
的类(class)是 TemplateClass<A>
在这个类的范围内,TemplateClass
指TemplateClass<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/