Cam*_*ron 34 c++ gcc templates name-lookup c++11
当编译器尝试解析i.template hi<T>();它时hi,在全局命名空间中找到而不是hi在i(ideone)上找到方法.为什么?
#include <cstdio>
// Define 'hi' and 'bye' in the global namespace; these should *not* be used
template<typename T> struct hi { };
template<typename T> struct bye { };
// Foo needs to be templated for Foo::Inner to be a dependent type (I think)
template<typename T>
struct Foo
{
struct Inner {
// This is a plain-old templated member function of Inner, yes?
template<typename U>
void hi() { std::printf("hi!\n"); }
// This is a plain-old member function of Inner
void bye() { std::printf("bye!\n"); }
};
void sayStuff()
{
Inner i;
i.template hi<T>(); // Fails to compile -- finds global hi instead of member
i.bye(); // Compiles fine, finds member
}
};
int main() {
Foo<int> f;
f.sayStuff();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用g ++ 4.9.1/4.9.2(-std=c++11).确切的错误消息:
prog.cpp: In member function 'void Foo<T>::sayStuff()':
prog.cpp:19:5: error: invalid use of 'struct hi<T>'
i.template hi<T>();
^
Run Code Online (Sandbox Code Playgroud)
此代码适用于Clang和VS2013,但在g ++和EDG中生成错误.但哪些编译器是对的?
除了更改会员名称之外,还有什么方法可以解决这个问题吗?在我的真实代码中,当std命名空间中的类型(例如,通过其导入using namespace std)与我的一个成员函数具有相同的名称时,就会发生冲突.显然,我希望我的实现代码是健壮的,不会导致用户代码中的随机名称冲突.
据我所知,这是正在发生的事情.
DR228说:
[2003年4月会议投票通过WP.]
请考虑以下示例:
template<class T>
struct X {
virtual void f();
};
template<class T>
struct Y {
void g(X<T> *p) {
p->template X<T>::f();
}
};
Run Code Online (Sandbox Code Playgroud)
这是一个错误,因为X不是成员模板; 14.2 [temp.names]第5段说:
如果以关键字模板为前缀的名称不是成员模板的名称,则该程序格式错误.
在某种程度上,这是完全合理的:即使p具有依赖类型,X也被发现是使用普通查找的模板.但是,我认为这使得使用模板前缀更难教.
这是故意禁止的吗?
拟议决议(4/02):
在14.2 [temp.names]第5段中首次使用"成员"一词,以便其第一句如下:
如果以关键字模板为前缀的名称不是模板的名称,则该程序格式错误.
但是,在C++标准N4296的最新公开草案中,以下措辞出现在§14.2.5中:
以关键字模板为前缀的名称应为template-id,或者名称应引用类模板.[ 注意:关键字
template可能不适用于类模板的非模板成员.-end note ] [ 注意:与typename前缀的情况一样,在template不是绝对必要的情况下允许使用前缀; 即,当嵌套名称说明符或在左侧的表达->或.上的不依赖模板的参数,或使用未出现在模板的范围.- 尾注 ][ 例如:
template <class T> struct A {
void f(int);
template <class U> void f(U);
};
template <class T> void f(T t) {
A<T> a;
a.template f<>(t); // OK: calls template
a.template f(t); // error: not a template-id
}
template <class T> struct B {
template <class T2> struct C { };
};
// OK: T::template C names a class template:
template <class T, template <class X> class TT = T::template C> struct D { };
D<B<int> > db;
Run Code Online (Sandbox Code Playgroud)
- 末端的例子 ]
这个措辞听起来很相似,但不同于挖掘.我发现在N3126草案中,措辞已改为此版本.
我能够将此更改链接回此DR96:
以下是14.2 [temp.names]第4和第5段中的措辞,其中讨论了以下"模板"关键字的使用.或 - >和合格的名称.
{}剪断
此功能的重点在于需要"template"关键字来指示"<"在某些上下文中开始模板参数列表.第5款中的限制可以对某些案件进行辩论.
首先,我认为应该更清楚的是,当在这些上下文中使用"template"关键字时,模板名称必须后跟模板参数列表.如果我们不明确这一点,我们将不得不添加几个语义澄清.例如,如果您说"p-> template f()",而"f"是包含模板和非模板的重载集:a)这是否有效?b)忽略过载集中的非模板?如果用户被迫写"p-> template f <>()",则很明显这是有效的,并且同样清楚的是忽略了重载集中的非模板.由于这个功能纯粹是为了提供句法指导,我认为重要的是它不会产生语义含义.
从本质上讲,DR228的微妙变化在随后的修订中丢失了; 然而,由于没有类似的限制,DR228的意图可能仍然存在,除非标准中有另一个修订版.这意味着在这种情况下模板查找必须全局发生,即使它是依赖类型.
让我们看看我们的名称查找规则§3.4.5.1:
在类成员访问表达式(5.2.5)中,如果
.或->标记后面紧跟着标识符后跟a<,则必须查找标识符以确定它是否<是模板参数列表的开头(14.2)或更少 - 运营商.首先在对象表达式的类中查找标识符.如果未找到标识符,则在整个postfix-expression的上下文中查找它,并命名一个类模板.
这似乎明确说明baz.foo->template bar<T>(); 我们将首先查看类上下文,这包括标准模板查找.完成后,如果没有找到任何内容,如果表达式的形式是正确的,我们跳转到整个表达式的上下文.本质上,它已经被提升,并且如果该行刚读完,那么该名称的查找必须以相同的方式执行,template bar<T>(); 尽管我们已经从DR228知道了这一点.我只是想仔细检查并确认.真正的问题是哪个模板应该优先考虑,全局范围内的模板或类范围中的模板.
为此我们现在需要询问非限定名称查找,因为现在bar正在考虑与foo相同的上下文中,因此它不再遵循成员查找规则,它遵循正常的,不合格的模板查找规则,当然,它更喜欢本地版本.
总而言之,似乎Clang和MSVC表现出正确的行为,而GCC和EDG在这种情况下并不存在.
我最好的猜测是为什么GCC有错误是在触发规则后选择错误的上下文来分配给表达式.而不是将上下文放在与postfix-expression相同的级别,而不是将其置于事故的全局级别?也许它只是跳过第一个查找步骤?(但这仅仅是猜测,我必须真正弄清楚在GCC源中要查看的位置.)这也可以解释为什么@Mikael Persson将查找更改为合格的解决方案导致编译重新开始.
来自提问者的链接错误报告让人们谈论为什么必须考虑全局范围,并且它应该是,但似乎很直接的是,本地范围匹配必须被赋予比全局范围更高的优先级.最近似乎还有一些活动.
| 归档时间: |
|
| 查看次数: |
709 次 |
| 最近记录: |