我以为我很了解名字查询(在观看了几个关于它的视频并阅读了很多内容之后),但我只是遇到了这个案例:
#include <iostream>
namespace test{
struct Id
{};
void do_something( const Id& ){ std::cout << "Hello, World!" << std::endl; }
class Test
{
public:
void do_something() { std::cout << "WTF!" << std::endl; }
void run()
{
Id id;
do_something( id ); // doesn't compile
}
};
}
int main()
{
test::Test my_test;
my_test.run();
}
指向的行不编译(在GCC4.8和VC11U2上),因为它试图使用成员函数test::Test::do_something()
而不是名称空间作用的test::do_something( const Id& )
,这似乎是唯一可能的候选者。
显然,成员函数名称隐藏了命名空间范围的名称,这对我来说是令人惊讶的,因为我记得在其他上下文中使用几乎相似的代码而没有产生此问题(但最终条件可能会有很大不同)。
我的问题是:这些编译器是否符合标准?
(遗憾的是,通过阅读标准文档很难理解名称查找,因此我需要专家确认)
答案 0 :(得分:6)
我的问题是:这些编译器是否符合标准?
是。在重载决策决定哪些函数可行候选(包括检查参数的数量)之前,编译器首先必须进行名称查找,以找到所有候选者,可行且不可行。在您的示例名称中,查找在成员do_something()
后停止,因此重载解析永远不会有机会决定命名空间范围是否可行。
3.4.1 [basic.lookup.unqual] / 1:“在3.4.1中列出的所有情况下,搜索范围按照每个相应类别中列出的顺序搜索声明;名称查找尽快结束作为名称的声明。“
3.4.1 [basic.lookup.unqual]第8段列出了搜索名称的上下文,甚至还有一个能够准确回答你问题的例子。 Test
的范围在封闭的命名空间之前被搜索,并且第1段说明“名称查找在名称”找到声明后立即结束。
答案 1 :(得分:4)
语言越是找不到构造的有效解释,错字或其他类似错误就越有可能导致编译器找到有效但错误的含义< / em>的。编译器假设如果在某个范围内定义了foo
,并且该范围内的代码使用foo
,程序员打算让代码使用范围内定义的foo
。如果程序员试图用foo
做一些内部范围定义不允许的事情,那么下列情况之一的几率是非常好的:
foo
不能支持它,因此必须找到内部foo
的其他一些操作或操作序列。可以支持。同样,除非程序员指出如何正确使用foo
,否则编译器无法生成良好的代码。只有当程序员的意图是#3时,编译器才可能生成符合预期行为的代码。然而,程序员真的打算#1或#2更有可能。如果编译器拒绝编译代码,即使假设#3会生成有效的代码,那么上述任何错误都将被找到并因此可以得到纠正。相比之下,如果编译器在可能的情况下假设为#3,那么如果程序员确实打算#1或#2问题在代码运行之前就不会出现,并且表现与设计相反。
顺便说一句,如果我有我的druthers,我会在.NET语言中将这个原则应用于区分大小写,不仅禁止以与定义不一致的方式编写任何标识符(如C#而不是vb)。 net),但是使用任何仅在内部范围内与上部/下部外壳不同的标识符。例如:class foo
{
int x;
void bar()
{
int X=2;
x=4; // ****
return X;
}
}
鉴于上面的代码,C#会猜测带有星号的行是为了写字段;给定类似的代码,vb.net会假设它是为了编写局部变量。就个人而言,我不喜欢这两种假设; &#34;准确地说出你的意思&#34;会告诉我编译器应该要求程序员说this.x=4;
或X=4;
,这两者都不可能被认为具有错误的含义。
答案 2 :(得分:2)
这根本不是查询问题。关键点是查找在重载决策开始之前完成。当编译器看到do_something
时,它执行查找以找出它意味着什么,它发现它是一个函数,反过来又激活ADL以发现其他潜在的过载。然后查找完成并开始重载解析。当重载解析失败时。
答案 3 :(得分:1)
编译器将收集所有“候选名称”,这些名称将来自同一范围,除非涉及ADL,然后尝试选择最佳匹配(如果有)。在任何情况下,失败的匹配都不会导致它尝试从备用范围中查找其他候选名称。
这与编译器首先执行重载解析非常类似,然后检查成员的公共/私有,看它是否实际可访问。
g ++有一个方便的-Wshadow
选项来追捕阴影(我不确定它会特别警告这一点)。
答案 4 :(得分:1)
遗憾的是,通过阅读标准文档很难理解名称查找,因此我需要专家确认
我无论如何都不是专家,但这就是我如何理解标准的名称查找规则。
两个例子:
void foo(int);
namespace associated
{
struct bee {};
void flower(bee);
}
namespace bar
{
void foo();
void flower();
void test()
{
foo(42); // (A)
flower(associated::bee()); // (B)
}
}
int main()
{
bar::test();
}
由于[basic.lookup.unqual],(A)无法编译:“一旦找到名称的声明,名称查找就会结束”
(B)因ADL而编译; associated
是一个关联的命名空间。
然而,有[basic.lookup.argdep] / 3:
设X是由非限定查找(3.4.1)生成的查找集,并且让Y是由参数相关查找生成的查找集(定义如下)。如果X包含
- 类成员的声明,或
- 块范围函数声明,它不是using声明或
- 既不是函数也不是函数模板的声明
然后Y为空。否则Y是在与参数类型相关联的名称空间中找到的声明集,如下所述。通过查找名称找到的声明集是X和Y的并集。
第一点适用于您的示例。因此,我认为是,拒绝您的示例的编译器符合标准。