同名友元函数和方法

Friend function and method with the same name

以下 classes 定义声明了一个友元函数,为其提供内联定义。我试图从 class 方法中调用与友元函数同名的友元函数,但为了使其工作,我必须从封闭的命名空间访问它(这也需要前向声明,class C 下面)。为什么名称查找适用于 class A 而在 class 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);
}

inlinefriend?为什么不 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);    
}

不管这是个好主意,下面是对失败原因的解释:

编译器使用几个不同的处理阶段来确定您的程序所说的内容。 class B 没有编译的原因是因为发生的失败发生在 friend 被注意到之前。让我解释一下:

当编译器开始尝试弄清楚 swap 的含义时,它会进行名称查找。它使用特定的规则来指定应该查看的位置。这是简化的,但基本上它首先查找在本地范围内定义的符号,然后在 class 范围内,然后在封闭(命名空间等)范围内。它找到在 class 范围内定义的那个,然后停止查找。 swap 没有采用这 2 个参数,因此编译失败。

friend 声明允许自由函数访问 B 的内部结构,作为您在全局中声明的 swap 函数的附加声明命名空间。如果编译器在名称查找中考虑全局命名空间中的函数,编译器将考虑这些声明。在classB中,编译器还没到这个阶段就已经停止处理了。 (并且 friend 声明在更晚的阶段是必要的,当编译器正在编译与 B 对象一起使用的 swap 函数版本时,并且想要弄清楚, "in this function called swap that can take these 2 parameters; can I access B's internals?")

在 class A 中,您使用了不同的名称。在找到您的免费​​ swap 函数之前,名称查找阶段不会成功。在 class C 中,您给出了名称查找特定说明,"hey, when you're looking up swap, look in global-namespace scope, and ignore the ones in local- and class-scope that you might find."

(注意:名称查找的描述和 friend 在@PeteBecker 的评论后更新。)