g ++ 4.5 не может найти функцию друга - PullRequest
3 голосов
/ 22 марта 2011

G'day!

У меня вопрос по поводу использования friend в C ++. Рассмотрим следующий фрагмент кода:

#include <ostream>

struct F {
};

struct N {
  friend std::ostream& operator<< (std::ostream&, const N&);
  friend std::ostream& operator<< (std::ostream&, const F&);    
};

void foo(std::ostream &out) {
  F bar;
  out << bar;
}

Я всегда понимал, что friend похож на static с дополнительным свойством, что функция friend имеет доступ к закрытой части класса. При таком предположении код должен компилироваться, поскольку существует operator<<, который принимает ostream& и (const) F&.

Похоже, что g ++ 4.0 разделяет мои мысли по этому поводу, поскольку он принимает этот код. Однако гораздо более новый g ++ 4.5 (.2) отклоняет код с сообщением:

ns.cc: In function 'void foo(std::ostream&)':
ns.cc:14:10: error: no match for 'operator<<' in 'out << bar'

не верно ли g ++ 4.5 или я (и g ++ 4.0) ошибочны?

(Решение переместить объявление друга в класс F не помогает, поскольку operator<< потребуется доступ к закрытой части N.)

С уважением, Stefan

Ответы [ 3 ]

5 голосов
/ 22 марта 2011

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

struct N {
   friend void func1() { }
   friend void func2();
   friend void func3();
};

void func3();

func1(); /* OK */
func2(); /* not OK */
func3(); /* OK */
4 голосов
/ 22 марта 2011

Вы должны также объявить операторы вне структуры. О той же ошибке сообщает gcc 4.4.

#include <ostream>

struct F {
};

struct N {
  friend std::ostream& operator<< (std::ostream&, const N&);
  friend std::ostream& operator<< (std::ostream&, const F&);    
};

std::ostream& operator<< (std::ostream&, const N&);
std::ostream& operator<< (std::ostream&, const F&);    

void foo(std::ostream &out) {
  F bar;
  out << bar;
}
0 голосов
/ 22 марта 2011

Я бродил по стандарту (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, чтобы контролировать его связь, но это будет редко иметь значение.

...