Функция Friend не видна в классе

У меня есть следующий код:

struct M {
    friend void f() {}
    M() {
        f(); // error: 'f' was not declared in this scope
    }
};

int main() {
    M m;
}

Живой пример

Оба g++ 4.8 и clang3.4 не могут скомпилировать его, потому что f не отображается внутри M, или, соответственно, они говорят.

Однако стандарт дает пример аналогичного кода

class M {
  friend void f() { } // definition of global f, a friend of M,
                      // not the definition of a member function
};

и говорит, что

Функция A friend, определенная в классе, находится в (лексическом) объеме класс, в котором он определен.

(ISO/IEC 14882: 2011 11.3 Друзья [class.friend] p6, p7)

Из этого я не могу понять, как компилятор не может найти f, который определен в том же классе, где он использовался.

Как маловероятно, что оба компилятора имеют одинаковую ошибку.
Итак, что я пропустил?

Ответ 1

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

Соответствующее правило - С++ 11 7.3.1.2/3:

Если объявление friend в нелокальном классе сначала объявляет класс или функцию, класс или функция друга является членом самого внутреннего охватывающего пространства имен. Имя друга не найдено по неквалифицированному поиску или по квалифицированному поиску до тех пор, пока в этой области пространства имен не будет представлено соответствующее объявление.

Ответ 2

Эта цитата из С++ Standard

Функция друга, определенная в классе, находится в (лексическом) объеме класс, в котором он определен.

означает следующее

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

Это поиск любого имени, используемого в функции, начиная с класса.

Однако сама функция не видна в пространстве имен, пока она не будет объявлена ​​вне класса.

Итак, в вашем случае достаточно объявить функцию до определения класса

void f() {}

struct M {
    friend void f();
    M() {
        f(); 
    }
};

int main() {
    M m;
}

или

void f();

struct M {
    friend void f() {}
    M() {
        f(); 
    }
};

int main() {
    M m;
}

Ответ 3

Критический вопрос заключается в том, при каких обстоятельствах компилятор может/может найти объявление вашей функции. Для общей функции friend вы должны объявить ее вне класса, чтобы компилятор смог ее найти.

Однако есть очень полезное исключение: если функция friend имеет аргумент типа класса, она может найти функцию без дополнительного объявления из-за поиск зависимых от аргументов.

Этот случай на самом деле очень важен, потому что обычно вам нужна функция friend для доступа к объекту типа класса.


Рассмотрим следующий пример:

#include <iostream>

struct M
{
    friend void printI(int a) {
        std::cout << a;
    }
    friend void print(const M& m) { // friend takes object of class type!
        std::cout << "M";
    }
    void foo() {
        printI(2); // ERROR - requires declaration!
        print(*this); // OK!
    }
};

int main()
{
    M m;
    m.foo();
    printI(2); // ERROR - requires declaration!
    print(m); // OK
}

Ответ 4

struct M {
    friend void f() {}
    M() {
        f(); // error: 'f' was not declared in this scope
    }
};

int main() {
    M m;
}

Приведенный выше код работает отлично. (проверено на DevС++) Также попробуйте не определять функцию внутри класса, так как она может не иметь видимой области вне ее, т.е. В main(). При попытке вызвать f() из main() вы получите сообщение об ошибке, поскольку функция не существует. Поэтому определите функцию внешних классов с помощью оператора :: (если необходимо), чтобы не было проблем с доступом к функции из любой точки. Функция доступа к другу, определенная в классе