考虑命名空间内的一个类。该类的定义声明了一个友元函数。
namespace Foo
{
class Bar
{
friend void baz();
};
}
根据我所知,这应该将baz()
声明为最里面的封闭命名空间的成员,即Foo
。
因此,我希望baz()
的以下定义是正确的:
void Foo::baz() { }
然而,GCC(4.7)给我一个错误。
error: ‘void Foo::baz()’ should have been declared inside ‘Foo’
有几种解决方案似乎有效:
在课堂外声明baz()
。
namespace Foo
{
void baz();
class Bar
{
friend void baz();
};
}
在命名空间内定义baz()
。
namespace Foo
{
class Bar
{
friend void baz();
};
}
...
namespace Foo
{
void baz() { }
}
使用-ffriend-injection
标志进行编译,从而消除错误。
这些解决方案似乎与我所知的C ++中的声明/定义的一般规则不一致。
为什么我必须两次声明baz()
?
为什么定义在命名空间内只是合法的,而且使用范围解析运算符是非法的?
为什么标志会消除错误?
答案 0 :(得分:4)
为什么我必须两次声明baz()?
因为friend声明没有在命名空间中提供函数的可用声明。它声明,如果该函数在该命名空间中声明,它将是一个朋友;如果你在一个类中定义一个友元函数,那么它将通过依赖于参数的查找(但不是其他方式)可用,就好像它是在命名空间中声明一样。
为什么定义在命名空间内只是合法的,而且使用范围解析运算符是非法的?
因为它没有在命名空间中(正确地)声明,并且只有声明了函数才能在其命名空间之外定义(使用范围解析)。
为什么标志会消除错误?
因为该标志导致friend声明在命名空间中充当声明。这是为了兼容古老的C ++方言(显然,一些现代编译器),这是标准的行为。
答案 1 :(得分:1)
第一段代码应编译:
namespace A {
struct B {
friend void foo();
};
}
void A::foo() {}
虽然除非您还在命名空间级别提供声明,否则不能使用特定功能。原因是友元声明只能通过Argument Dependent Lookup(ADL)看到,但foo
不依赖于A::B
,因此编译器永远不会查看该类型。