Я бродил по стандарту (FCD, n3242) с тех пор, как увидел вопрос
В [class.friend] можно прочитать:
6) Функция может быть определена в объявлении друга класса, если и только если класс является нелокальным классом (9.8), имя функции неквалифицированное, и функция имеет область пространства имен.
7) Такая функция неявно встроенная. Функция друга, определенная в классе, находится в (лексической) области действия класса, в котором она определена. Функция друга, определенная вне класса, не является (3.4.1).
9) Имя, назначенное объявлением друга, должно быть доступно в области видимости класса, содержащего объявление друга.
Итак, что здесь происходит?
struct F {
};
struct N {
friend std::ostream& operator<< (std::ostream&, const F&);
};
Объявление друга назначает эту перегрузку operator<<
как друга N
. Однако эта перегрузка не была объявлена в лексической области (пространстве имен или классе). Кроме того, 7
не применяется, поскольку оно также не определено в N
.
Следовательно, при поиске перегрузок operator<<
, которые могут применяться в:
void foo(std::ostream &out) {
F bar;
out << bar;
}
Нет допустимой перегрузки (на самом деле, может быть, перегрузки вообще нет).
У вас есть два решения:
- использовать
7
: определить встроенную функцию, следуя объявлению друга.
- использовать
9
: объявить функцию также в пространстве имен
Из-за 4
, хотя:
4) Функция, впервые объявленная в объявлении друга, имеет внешнюю связь (3.5). В противном случае функция сохраняет прежнюю связь (7.1.1).
Я бы рекомендовал объявить его до декларацией friend
, чтобы контролировать его связь, но это будет редко иметь значение.