11

最小的例子:

class A
{
    friend void swap(A& first, A& second) {}
    void swap(A& other) {}
    void call_swap(A& other)
    {
        swap(*this, other);
    }
};

int main() { return 0; }

g++ 4.7 说:

friend.cpp: In member function ‘void A::call_swap(A&)’:
friend.cpp:7:20: error: no matching function for call to ‘A::swap(A&, A&)’
friend.cpp:7:20: note: candidate is:
friend.cpp:4:7: note: void A::swap(A&)
friend.cpp:4:7: note:   candidate expects 1 argument, 2 provided

结果第 4 行:

// void swap(A& other) {}

......它工作正常。如果我想保留交换函数的两个变体,为什么以及如何解决这个问题?

4

6 回答 6

8

我相信这是因为编译器试图在类中找到函数。这应该是使其工作的极简更改(它在 Visual Studio 2012 中工作):

class A; // this and the next line are not needed in VS2012, but
void swap(A& first, A& second); // will make the code compile in g++ and clang++

class A
{
    friend void swap(A& first, A& second) {}
    void swap(A& other) {}
    void call_swap(A& other)
    {
        ::swap(*this, other); // note the scope operator
    }
};

int main() { return 0; }
于 2013-08-26T19:15:35.667 回答
7

作为一种解决方法,您可以static声明swap. 然后,您可以声明friend版本以调用static版本。

class A
{
public:
    friend void swap(A& first, A& second) { A::swap(first, second); }
private:
    static void swap(A& first, A& second) {}
    void swap(A& other) {}
    void call_swap(A& other)
    {
        swap(*this, other);
    }
};

int main () {
    A a, b;
    swap(a, b);
}
于 2013-08-26T17:59:17.977 回答
7

为什么

在类内部,类范围内的名称隐藏了周围命名空间中的名称;所以朋友(其名称在命名空间中,但不能直接访问)被成员隐藏(在类中)并且在此处作为潜在的重载不可用。(更新:或者可能比这更复杂一点,正如评论中提到的那样。范围和名称查找规则有点难以遵循,尤其是当涉及到朋友时)。

如果我想保留交换函数的两个变体,如何解决这个问题?

没有完美的解决方案。如果两个函数都做同样的事情,那么只需使用其他成员函数中的成员。如果您在类定义之外声明朋友,那么它可以作为::swap; 但是如果将类放在不同的命名空间中,这有点脆弱。(更新:或使用this answer建议的静态成员函数;我没有想到)。

于 2013-08-26T17:57:19.407 回答
3

保持标准的交换习惯,你不会有问题:

void call_swap(A& other) {
  using std::swap;
  swap(*this, other);
}

或者使用 Boost.Swap 包装器:

void call_swap(A& other) {
  boost::swap(*this, other);
}

这几乎等同于@Juan 的解决方案,只是您不是自己编写助手。

于 2013-08-26T18:17:02.133 回答
0

您在这里观察到的是,如果没有先前的friend函数声明,friend则类中的 ship 会将名称注入封闭的 namespace,但不会注入类范围。在类范围内发生的唯一事情是命名的函数被授予访问私有属性的权限。

这仅在类范围内留下一个交换函数(具有一个参数的成员),因此这是唯一的候选重载。一旦你找到了一个候选人,即使重载决议失败,你也永远不会尝试另一个封闭范围(阴影)。

如果您确实需要这两个版本(并退后一步以确保您确实需要),请将实现放入swap_impl您从朋友和成员那里调用的函数中。

于 2013-08-26T18:17:43.137 回答
0

您还可以使用辅助函数,如本例所示

template<class T>
void swap_elems(T& lhs, T& rhs)
{
    using namespace std;
    swap(lhs, rhs);
}

class A
{
friend void swap(A& first, A& second) { first.swap(second); }

  public:
    void swap(A& other) {}
    void call_swap(A& other)
    {
        swap_elems(*this, other);
    }
};
于 2013-08-26T18:13:27.137 回答