以下类定义声明了一个友元函数,为其提供内联定义。我试图从具有相同名称的友元函数的类方法调用friend函数,但为了使其工作,我必须从封闭的命名空间访问它(这也需要一个前向声明,下面的类C
)。为什么名称查找适用于类A
,它在类B
中不起作用?请注意,B::swap
的参数与其友元函数的参数不同。
#include <utility>
struct A {
A(int x) : v{ x } {}
friend void swap(A& x, A& y) { std::swap(x.v, y.v); }
void swapm(A& other) { swap(*this, other); }
private:
int v;
};
struct B {
B(int x) : v{ x } {}
friend void swap(B& x, B& y) { std::swap(x.v, y.v); }
void swap(B& other) { swap(*this, other); } // <-- This doesn't compile
private:
int v;
};
struct C;
void swap(C& x, C& y);
struct C {
C(int x) : v{ x } {}
friend void swap(C& x, C& y) { std::swap(x.v, y.v); }
void swap(C& other) { ::swap(*this, other); }
private:
int v;
};
int main()
{
A a1{ 1 }, a2{ 2 }; swap(a1, a2); a1.swapm(a2);
B b1{ 3 }, b2{ 4 }; swap(b1, b2); b1.swap(b2);
C c1{ 5 }, c2{ 6 }; swap(c1, c2); c1.swap(c2);
}
无论这是否是一个好主意,这里都解释了为什么它失败了:
编译器使用几个不同的处理阶段来确定您的程序所说的内容。类B
不编译的原因是因为在friend
被注意到之前发生的失败。让我解释:
当编译器到达试图找出swap
意味着什么的点时,它会进行名称查找。它使用特定的规则来指定它应该看起来的位置。这是简化的,但基本上它首先查找在本地作用域中定义的符号,然后在类作用域中查找,然后在封闭(名称空间等)作用域中查找。它找到在类范围中定义的那个,并停止查找。 swap
没有采用这两个参数,所以编译失败了。
friend
声明允许自由函数访问B
的内部,它作为你在全局命名空间中声明的swap
函数的附加声明。如果编译器在名称查找中考虑了全局命名空间中的函数,则会考虑这些声明。在类B
中,编译器在进入此阶段之前已经停止了处理。 (并且friend
声明在更晚的阶段是必要的,当编译器正在编译与swap
对象一起使用的B
函数的一个版本时,并且想要弄清楚,“在这个函数中称为swap
可以采取这些2参数;我可以访问B
的内部吗?“)
在课堂上A
,你使用的是另一个名字。在找到您的免费swap
函数之前,名称查找阶段不会成功。在类C
中,您已经给出了特定于名称查找的指令,“嘿,当您查找swap
时,查看全局命名空间范围,并忽略您可能找到的本地和类范围中的那些。”
(注意:@ PeteBecker评论后更新名称查找和friend
的说明。)
inline
friend
?为什么不在这种情况下static
?而不是friend
?然后创建一个调用static
的全局变量。对我来说,这是一个糟糕的设计,而不是一个实际的问题。
方法swap
应该是做工作的人而不是friend
(因为不再需要朋友):
struct C {
C(int x) : v{ x } {}
void swap(C& other) { std::swap(this->v, other.v); }
private:
int v;
};
void swap(C& x, C& y)
{
x.swap(y);
}