请考虑以下代码:
namespace Foo1 {
void add( int ) {}
void subtract( int ) {}
}
namespace Foo2 {
class Bar {
public:
friend void add( Bar ) {}
};
void subtract( Bar ) {}
void xxx() {
int i = 0;
using namespace Foo1;
add( i ); // Is this an error or not?
subtract( i ); // This is an error due to name hiding
}
}
在Foo2::xxx()
中,我使用using命名空间来访问Foo1::add
和Foo1::subtract
。调用减法显然是一个错误,因为Foo2::subtract
隐藏名称。但是Foo2::add
不应真正在Foo2
中可见,因为它只能可以使用ADL找到,并且不应隐藏Foo1::add
。我的理解正确吗?
我已经在MSVC和gcc的多个版本上尝试了上述代码。前者一直以来拒绝了add(i)
通话,但错误消息对我来说仍然不清楚。后者一直接受它。以下哪个(如果有)是正确的?
我认为海湾合作委员会就在这里。
[namespace.memdef](强调我的意思)
3如果非本地类中的朋友声明首先声明了类,函数,类模板或函数模板,朋友是最里面的封闭名称空间的成员。 朋友声明本身不会使名称对不合格的查找可见([basic.lookup.unqual])或限定查询([basic.lookup.qual])。[注意:如果匹配声明在名称空间范围内提供(之前或之后在课堂定义授予友谊之后)。 —注释]如果朋友函数或函数模板被调用,其名称可能会找到通过名称查找考虑名称空间中的函数与函数参数类型关联的类([basic.lookup.argdep])。
因此,不合格的add( i )
自身不应该找到add( Bar )
的声明,这意味着查找应继续并考虑using指令引入的名称。而且由于参数不是类类型的,因此ADL是不可能的。我的结论是add( Bar )
不应隐藏add( int )
。
正如已经指出的(C ++ 20标准,9.7.1.2命名空间成员定义)
3如果非本地类中的朋友声明首先声明了一个类,函数,类模板或函数模板100 朋友是一个最内部封闭的名称空间的成员。
因此,此朋友函数add
是名称空间Foo2的成员。它在名称空间Foo2中不可见,直到相应的函数声明出现在名称空间Foo2中。并且只能由于依赖于参数的查找而找到。
namespace Foo2 {
class Bar {
public:
friend void add( Bar ) {}
};
//..
如果您在函数xxx
之前编写,则将从名称空间Foo1添加的名称隐藏起来>
namespace Foo2 { class Bar { public: friend void add( Bar ) {} }; void add( Bar ); //...
在功能
xxx
内
void xxx() { int i = 0; using namespace Foo1; add( i ); // Is this an error or not? subtract( i ); // This is an error due to name hiding }
编译器按以下顺序考虑名称的减法。首先,它会浏览作为名称空间
Foo2
的封闭名称空间。并且此命名空间具有声明的名称substract
。因此,搜索过程停止。找不到重载的函数void substract( int )
,因为由于using指令,它被视为全局命名空间的成员。该名称空间包含使用指令中指定的名称空间和包含使用指令的名称空间。
您可以通过以下方式考虑它(由于使用Directibe)
// the global namespace
void subtract( int ) {}
namespace Foo2
{
class Bar {
public:
friend void add( Bar ) {}
};
void subtract( Bar ) {}
void xxx() {
int i = 0;
// using namespace Foo1;
add( i ); // Is this an error or not?
subtract( i ); // This is an error due to name hiding
}
}