在搜索到SO之后,一个问题告诉我内联友元函数的词汇范围是它定义的类,这意味着它可以访问例如班级中的typedef
没有符合条件。但后来我想知道这个函数的实际范围是什么? GCC至少拒绝我所有的调用它的尝试。可以通过ADL以外的方式调用例如示例中的函数,由于没有参数,这在这里是不可能的吗?
标准报价表示赞赏,因为我目前无法访问我的副本。
namespace foo{
struct bar{
friend void baz(){}
void call_friend();
};
}
int main(){
foo::baz(); // can't access through enclosing scope of the class
foo::bar::baz(); // can't access through class scope
}
namespace foo{
void bar::call_friend(){
baz(); // can't access through member function
}
}
会导致这些错误:
prog.cpp: In function ‘int main()’:
prog.cpp:9: error: ‘baz’ is not a member of ‘foo’
prog.cpp:10: error: ‘baz’ is not a member of ‘foo::bar’
prog.cpp: In member function ‘void foo::bar::call_friend()’:
prog.cpp:15: error: ‘baz’ was not declared in this scope
答案 0 :(得分:35)
当您在类中声明具有非限定id的friend
函数时,它会在最近的封闭命名空间范围内命名一个函数。
如果之前未声明该函数,则friend
声明不会使该函数在该范围内可见,以进行正常查找。它确实使声明的函数对依赖于参数的查找可见。
许多注释都强调了这一点,但最终的陈述是在7.3.1.2/3(ISO / IEC 14882:2011)中:
首先在名称空间中声明的每个名称都是该名称空间的成员。如果非本地类第一个中的
friend
声明声明了一个类或函数,那么友元类或函数是最内层封闭命名空间的成员。 无法通过非限定查找(3.4.1)或限定查找(3.4.3)找到朋友的名称,直到在该命名空间范围内提供匹配声明(在类定义之前或之后)给予友谊)。如果调用了友元函数,则可以通过名称查找找到其名称,该名称查找考虑名称空间中的函数和与函数参数类型相关联的类(3.4.2)。如果friend
声明中的名称既不合格也不是 template-id ,并且声明是函数或 elaborated-type-specifier ,则查找确定该实体先前是否已被声明,不应考虑最内层封闭命名空间之外的任何范围。
答案 1 :(得分:4)
“C ++编程语言第3版(Stroustrap)”:p279:
予。 “就像成员声明一样,朋友声明不会在封闭范围中引入名称”
II。 “友元类必须先在封闭范围内声明,或在非类中定义 范围立即附上宣布为朋友的类“
III。 “友元函数可以像朋友类一样显式声明,或者可以通过其参数类型(第8.2.6节)找到它,就好像它是在非类中声明的那样 范围立即封闭其类。“
IV。 “因此,友元函数应该在封闭范围内显式声明,或者取其类的参数。如果不是,则不能调用该朋友。例如:”
//no f() here
void g();
class X{
friend void f(); //useless
friend void g(); //can be found because it is declared outside of class scope
friend void h(const X&); //can be found because the arguments access class members
};
void f() { } //enemy of X :)
但在你的情况下,它与命名空间有更多关系,因为如果你把正确的声明放在foo中,例如:
namespace foo{
struct bar{
friend void baz(const &bar){};
void call_friend();
}
}
无法编译。但是,如果你在foo之外声明它就像魅力一样。现在考虑事实上,全局,本地,结构和类实际上是名称空间。现在,这导致了baz(const &)
在全局范围内隐式定义的结论。
编译:
namespace foo{
struct bar{
friend void baz(const bar&){};
void call_friend();
};
}
int main(){
foo::bar k;
baz(k);
return 0;
}
因此,有两个问题:
谢谢,希望它有所帮助,当然,我喜欢挑战:)。
答案 2 :(得分:2)
有趣!
似乎编译器不知道它属于哪个范围(并且说实话没有线索),因此不在任何范围内。我猜想会有一些标准的挖掘工作。
注意:如果您明确地向特定范围添加声明,那么它将按预期开始工作。
namespace foo
{
void baz(); // declare it here and now it works in foo namespace etc.
struct bar
{
friend void baz(){}
void call_friend();
};
}
挖掘我发现的标准:
当且仅当该类是非本地类(9.8),函数名称不合格且函数具有命名空间范围时,才能在类的友元声明中定义函数。
[ Example:
class M { friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
— end example ]
这样的功能是隐式内联的。在类中定义的友元函数位于类的(词法)范围内,在其中定义它。在课外定义的朋友函数不是(3.4.1)。
不带参数的独立功能作为朋友没什么用处。因为它没有利用其友谊的对象(我认为文件范围是静态存储持续时间对象)。
答案 3 :(得分:0)
在本例中,
namespace foo{
struct bar{
friend void baz(){}
void call_friend();
};
}
int main(){
foo::baz(); // can't access through enclosing scope of the class
foo::bar::baz(); // can't access through class scope
}
namespace foo{
void bar::call_friend(){
baz(); // can't access through member function
}
}
foo::baz()
无法访问,因为名称baz
在名称空间foo
的范围内不可见。如果我没记错的话(§3.4/ 2)适用于此。
foo::bar::baz()
无法访问,因为朋友不是类的成员,并且内联友元函数的范围是名称空间或类,因此,您不能在该范围之外访问它们。
如果您将baz()
的声明放在foo
中,则baz
中将显示函数foo
的名称,并且将在嵌套范围内查找定义bar
。
namespace foo{
void baz(); // declaration at namespace scope
struct bar{
friend void baz(){}
};
void call_friend() {
baz(); // ok
}
}
int main()
{
foo::baz(); // ok, bar will be looked up in the nested scope of foo::bar.
}
答案 4 :(得分:0)
在封闭类中定义的 friend
函数只能通过参数依赖查找 (ADL) 找到。当一个或多个参数是封闭类类型时,调用它将成功;或在类中声明的类型。这是一个示例,显示“Hello World!”,它(对于各种)不使用封闭类类型的对象来提供这样的参数:
#include <iostream>
struct foo
{
struct local_class{};
friend void greet(local_class o, int) { std::cout << "Hello World!\n"; }
};
int main(int argc, char *argv[])
{
foo::local_class o;
greet(o,1);
return 0;
}
答案 5 :(得分:-2)
我认为你让friend
和private
感到困惑。通过声明函数friend
,您授予它访问您的私有成员的权限,而不是让其他函数访问它。无论如何,任何对象都可以访问struct
的任何成员函数,因为struct
成员默认是公共的。
但是,在您的情况下,baz
无法访问,因为通过执行friend void baz(){}
您并未真正声明函数baz
,您只是说它是friend
功能。您只需删除friend
关键字即可解决所有问题。