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
Run Code Online (Sandbox Code Playgroud)class-name-or-namespace-name::...
[...]
这就是这种情况.该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应引用同一实体.
在我们的计划中并非如此.程序格式错误,编译器需要提供诊断消息.
模糊性保持如果您更换同B
用TemplateClass<A>
,如使用的问题.
在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错误.
归档时间: |
|
查看次数: |
1855 次 |
最近记录: |