Функция и метод друга с тем же именем - PullRequest
0 голосов
/ 11 марта 2019

В следующих определениях классов объявляется функция 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);
}

Ответы [ 2 ]

4 голосов
/ 11 марта 2019

Является ли это хорошей идеей или нет, вот объяснение причины ее сбоя:

Компилятор использует несколько различных этапов обработки, чтобы выяснить, что говорит ваша программа.Причина, по которой класс B не компилируется, заключается в том, что происходящий сбой происходит до того, как будет замечен friend.Позвольте мне объяснить:

Когда компилятор пытается выяснить, что означает swap, он выполняет поиск по имени.Он использует определенные правила, которые указывают, где он должен выглядеть.Это упрощено, но в основном сначала выполняется поиск символов, определенных в локальной области видимости, затем в области видимости класса, а затем во вложенных областях (пространства имен и т. Д.).Он находит тот, определенный в области видимости класса, и перестает искать.swap не принимает эти 2 параметра, поэтому компиляция не удалась.

Объявление friend, которое позволяет свободной функции получать доступ к внутренним компонентам B, действует как дополнительное объявление дляswap функция, которую вы объявили в глобальном пространстве имен.Эти объявления будут рассмотрены компилятором, если он дойдет до точки поиска имени, где он рассматривает функции в глобальном пространстве имен.В классе B компилятор уже прекратил обработку, прежде чем он достигнет этой стадии.(И объявление friend было бы необходимо на более позднем этапе, когда компилятор работает над компиляцией версии функции swap, которая работает с B объектами и хочет выяснить, "в этой функции, называемойswap, который может принимать эти 2 параметра; могу ли я получить доступ к внутренним элементам B? ")

В классе A вы используете другое имя.Фаза поиска имени не будет успешной, пока она не найдет вашу бесплатную функцию swap.В классе C вы дали конкретные инструкции по поиску имен: «эй, когда вы ищите swap, посмотрите в области глобальных пространств имен и игнорируйте те, что в локальной и области классов, которые выможет найти. "

(Примечание: описание поиска имени и friend обновлено после комментария @ PeteBecker.)

0 голосов
/ 11 марта 2019

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);    
}
...