Функция доступа к другу, определенная в классе

Существует такой код:

#include <iostream>

class A{

public:
    friend void fun(A a){std::cout << "Im here" << std::endl;}
    friend void fun2(){ std::cout << "Im here2" << std::endl; }
    friend void fun3();
};

void fun3(){
    std::cout << "Im here3" << std::endl;
}

int main() 
{  
    fun(A()); // works ok
    //fun2(); error: 'fun2' was not declared in this scope
    //A::fun2(); error: 'fun2' is not a member of 'A'
    fun3(); // works ok
} 

Как получить доступ к функции fun2()?

Ответ 1

class A{

public:
    friend void fun(A a){std::cout << "Im here" << std::endl;}
    friend void fun2(){ std::cout << "Im here2" << std::endl; }
    friend void fun3();
};

Хотя ваше определение fun2 определяет "глобальную" функцию, а не член, и одновременно делает ее friend of A, вам все равно не хватает декларации той же функции в самой глобальной области.

Это означает, что никакой код в этой области не имеет представления о существовании fun2.

То же самое происходит и для fun, за исключением того, что поиск аргументов зависит от функции, потому что есть аргумент типа A.

Я рекомендую вместо этого определять свои функции обычным способом:

class A {
   friend void fun(A a);
   friend void fun2();
   friend void fun3();
};

void fun(A a) { std::cout << "I'm here"  << std::endl; }
void fun2()   { std::cout << "I'm here2" << std::endl; }
void fun3();

Обратите внимание, что все работает (кроме fun3, потому что я никогда не определял его).

Ответ 2

Причина, по которой вы можете вызвать fun, заключается в том, что объявление друга внутри класса A делает его видимым только с помощью зависимого от аргумента поиска. В противном случае объявления друзей не делают функции, которые они объявляют автоматически видимыми вне области класса, где они появляются.

Вам нужно добавить объявление в область пространства имен или внутри main, чтобы сделать fun2 видимым в main.

например.

void fun2();

fun3 отображается внутри main, потому что его определение (вне класса) также является объявлением, которое делает его видимым из main.

ISO/IEC 14882: 2011 7.3.1.2:

Имя друга не определяется неквалифицированным поиском (3.4.1) или квалифицированным поиском (3.4.3) до тех пор, пока в этой области пространства имен не будет представлено соответствующее объявление (до или после определения класса, дающего дружбу).

3.4.2 (Поиск по зависимостям, зависящим от аргумента)/4:

Любые функции имени пространства имен или шаблоны функций друзей, объявленные в связанных классах, видны в пределах их соответствующих пространств имен, даже если они не видны во время обычного поиска (11.3).