С++ - должны быть определены функции друзей в файле заголовка?

Я хочу перегрузить оператор < < в одном из моих классов. Подпись выглядит следующим образом:

friend std::ostream& operator<<(std::ostream& os, const Annuaire& obj)

Когда я пытаюсь определить его в файле .cpp, он говорит, что оператор < < точно принимает 1 аргумент, однако, когда я определяю его в .h, он скомпилирован/отлично работает.

Вот как я определяю его в файле .cpp:

std::ostream& Annuaire::operator<<(std::ostream& os, const Annuaire& obj){ // ... }

Имеет ли какое-либо отношение к функциям друга, которые должны быть определены в файлах заголовков?

Ответ 1

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

// .h and in class
friend std::ostream& operator<<(std::ostream& os, MyClass const& v);

// .cpp
std::ostream& operator<<(std::ostream& os, MyClass const& v){
    // print it
}

Ответ 2

Проблема заключается в том, как вы ее определяете. Это не член класса, это просто друг класса. Вам нужно отбросить префикс Annuaire::. Итак, измените это:

std::ostream& Annuaire::operator<<(std::ostream& os, const Annuaire& obj){ // ...

:

std::ostream& operator<<(std::ostream& os, const Annuaire& obj){ // ...

Причиной сообщения об ошибке является то, что Annuaire::operator<<(std::ostream& os, const Annuaire& obj) ожидает три аргумента: экземпляр Annuaire, на который он вызвал (как this), и два дополнительных аргумента (os и obj).

Ответ 3

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

std::ostream& Annuaire::operator<<(std::ostream& os, const Annuaire& obj){

Это определение будет для функции-члена Annuaire, называемой operator<<, которая принимает два аргумента, что недопустимо, поскольку operator<< может быть перегружен одним из двух способов: как свободная функция, принимающая два аргумента ( левой стороны и правой стороны) или как функция-член класса, которая появляется в lhs выражения, принимающего аргумент типа rhs. В этом конкретном случае, поскольку lhs std::ostream, и вы не можете его изменить, вы остаетесь с единственной возможностью использования свободной функции:

std::ostream& operator<<(std::ostream& os, const Annuaire& obj)

Ответ 4

Нет такого ограничения; вы, вероятно, просто пишете это неправильно. Должно быть что-то вроде этого:

class Foo
{
   int n;

   friend std::ostream & operator<<(std::ostream &, Foo const &);
};

std::ostream & operator<<(std::ostream & o, Foo const & x)
{
   return o << x.n;
}

Ответ 5

Как упоминалось в ответе Дэвида, в этом случае оператор не является функцией-членом, он просто является функцией друга в том же пространстве имен. Это указывало мне в правильном направлении в решении очень близкой проблемы.

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

Не должно быть релевантным, но я использую VS2013.

//Foo.h
namespace Bar{
    class Foo
    {
    public:
        Foo();
    private:
        int n;
        friend std::ostream & operator<<(std::ostream &, Foo const &);
    };
}

//Foo.cpp
using namespace Bar; //won't apply to the operator definition
Foo::Foo(){}// doesn't require the Bar qualifier because of the using-directive

//the operator required the Bar namespace qualifier
std::ostream & Bar::operator<<(std::ostream & o, Foo const & x)
{
    return o << x.n;
}