不明确的成员访问表达式:Clang 是否拒绝有效代码?
我有一些代码,出于这个问题的目的,归结为
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 trunk 拒绝它并显示以下错误消息:
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();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我相信 clang 正确地拒绝了这段代码。
clang 发现的歧义可以用一个不太复杂的示例来重现:
这里省略了模板中的继承,并使用两个独立的类进行实例化。 clang 产生的错误保持不变。
首先,在
TemplateClass
范围内,由于类名注入,名称TemplateClass
引用了TemplateClass
。这就是静态方法可以使用TemplateClass::method
而不是更显式的TemplateClass::method
的原因。用于解释静态方法中的
u.TemplateClass::method
的名称查找在“3.4.5类成员访问[base.lookup.classref]”中定义C++11 和 C++98 标准。相关部分是3.4.5/4:
形式的限定id,这里就是这种情况。 id-expression 是
.
右侧的部分,在我们的例子中,这是限定名称TemplateClass::method
。“整个后缀表达式的作用域”是静态函数的主体,在这个静态函数中
TemplateClass
指的是TemplateClass
,因为该函数是该类的成员(我们称为TemplateClass::static_method
)。因此在此范围内,名称指的是
TemplateClass
。“对象表达式”是
.
左边的部分,在我们的例子中是c
。c
的类是TemplateClass
,在该类的范围内,TemplateClass
引用TemplateClass
代码>.
因此,根据查找所使用的范围,该名称引用不同的实体。
现在标准说:
我们的程序中并非如此。该程序格式错误,需要编译器给出诊断消息。
如果将
B
替换为TemplateClass
(如问题中所用),歧义性保持不变。I believe that clang is correctly rejecting this code.
The ambiguity that clang finds can be reproduced with a less complicated example:
Here the inheritance in the template is omitted and two independent classes are used for the instantiations. The error produced by clang remains the same.
First of all, in the scope of
TemplateClass<T>
the nameTemplateClass
refers toTemplateClass<T>
, due to class name injection. This is the reason that the static method can useTemplateClass::method
instead of a more explicitTemplateClass<T>::method
.The name lookup used to interpret
u.TemplateClass::method
in the static method is defined in "3.4.5 Class member access [base.lookup.classref]" of the C++11 and C++98 standards.The relevant part is 3.4.5/4:
This is the case here. The id-expression is the part to the right of the
.
and in our case this is the qualified nameTemplateClass::method
.The "scope of the entire postfix-expression" is the body of the static function, and in this static function
TemplateClass
refers toTemplateClass<B>
, since the function is a member of that class (we calledTemplateClass<B>::static_method
).So in this scope the name refers to
TemplateClass<B>
.The "object expression" is the part left of
.
, in our casec
. The class ofc
isTemplateClass<A>
and in the scope of this class,TemplateClass
refers toTemplateClass<A>
.So, depending on the scope used for the lookup, the name refers to a different entity.
The standard now says:
This is not the case in our program. The program is ill-formed, and the compiler is required to give a diagnostic message.
The ambiguity stays the same if you replace
B
withTemplateClass<A>
, as used in the question.在 ISO/IEC 14882:2011(E) 中,“14.6.1 本地声明的名称 [temp.local]”,[#5] 说:
这使我相信在您的示例中
u.TemplateClass::method();
相当于u.TemplateClass::method();
如果 Clang 在一种情况下给出错误,而在另一种情况下编译干净,那么它就是一个 Clang 错误。In ISO/IEC 14882:2011(E), "14.6.1 Locally declared names [temp.local]", [#5] says:
This leads me to believe that in your example
u.TemplateClass::method();
is equivalent tou.TemplateClass<T>::method();
and if Clang gives an error in one case and compiles cleanly in the other case, then it's a Clang error.当我们调用这两行时:
那么类型参数 U 就是对象 c 的类型:
让我们离开
static_method
,并做一个实验:输出是:
在所有四种情况下(并且在
static_method
)我们调用TemplateClass::method
,并且在u.
和::
之间给出的类型名称将给出实际类型 T:TemplateClass >::method
到TemplateClass::method
。我不知道这种行为是否是 C++ 标准的一部分。
编辑:
实际上情况 #3 不是强制转换,这些是限定名称。总之,Clang 不知道这种限定语法,而 GCC 和 Visual C++ 2010 都知道。
When we call these two lines:
then the type argument U is the type of the object c:
Let's leave
static_method
, and do an experiment:The output is:
In all four cases (and inside
static_method
) we callTemplateClass<T>::method
, and the type name given betweenu.
and::
will give the actual type T:TemplateClass<TemplateClass<EmptyClass> >::method
toTemplateClass<EmptyClass>::method
.I don't know whether this behavior is part of the C++ standard.
EDIT:
Actually case #3 is not casting, these are qualified names. So in conclusion, Clang is not aware of this qualification syntax, while both GCC and Visual C++ 2010 are.
不是答案,
只是我的小贡献:
删除模板,但保留相同的名称:
给出
From N3242
本地声明的名称 [temp.local]
(...)
(...)
Not an answer,
just my small contribution:
Removing templates, but keeping the same names:
gives
From N3242
Locally declared names [temp.local]
(...)
(...)
由于从未使用过 Clang,我对这个问题非常感兴趣。 (具有讽刺意味的是,是的,我知道。)
Clang C++ 兼容性 表明有一些关于模板的事情其他编译器(特别是 GCC)处理它会抱怨的。这些是标准中定义较弱的事情(“好吧,你不应该允许这个......但你可以”);几乎所有这些都涉及模板。这些问题看起来都不像您的问题,但它们足够接近,可以提供丰富的信息,当然值得一读。
所以,看起来 Clang 并没有坏掉——只是 Clang 比其他的更挑剔。
Having never used Clang, I was quite interested in this problem. (Ironic, yes I know.)
Clang C++ Compatibility indicates that there are several things regarding templates that other compilers (notably GCC) process that it will complain about. These are things that are weakly defined in the standard ("well, you shouldn't allow this ... but you can"); nearly all of them involve templates. None of these exactly look like your problem, but they're close enough to be informative -- and certainly worth a read.
So, it doesn't look like Clang is broken -- it's just that Clang is pickier than the others.
我认为歧义是因为
TemplateClass
在继承中是两次TemplateClass : (TemplateClass : EmptyClass)
是否
u.TemplateClass::method();
意思是u.TemplateClass >::method();
或u.TemplateClass; >::method();
?也许GCC有标准权利,但无论如何你都应该添加
。I think the ambiguity is because
TemplateClass
is twice in the inheritanceTemplateClass : (TemplateClass : EmptyClass)
Does
u.TemplateClass::method();
meanu.TemplateClass<TemplateClass<EmptyClass> >::method();
oru.TemplateClass<EmptyClass> >::method();
?Perhaps GCC has the standard right, but whatever the case is you should add the
<T>
.