Не могу перегрузить оператор << как функцию-член - PullRequest
17 голосов
/ 22 марта 2012

Я пытаюсь перегрузить operator<< как функцию-член.Это работает, если просто сделать это:

friend ostream& operator<<(ostream& os, const MyClass& myClass); в моем заголовочном файле и в моем файле MyClass.cc:

ostream& operator<<(ostream& os, const MyClass& myClass)
{
   return myClass.print(os);
}

Однако, если я попытаюсь снять friend исделайте его функцией-членом, а затем он жалуется, что operator<< может принимать только один аргумент.Почему?

ostream& MyClass::operator<<(ostream& os, const MyClass& myClass)
{
   return myClass.print(os);
}

Я прочитал на этот вопрос , что он не может быть функцией-членом, но не уверен, почему?

Ответы [ 3 ]

44 голосов
/ 22 марта 2012

При перегрузке в качестве функции-члена a << b интерпретируется как a.operator<<(b), поэтому для него требуется только один явный параметр (с this в качестве скрытого параметра).

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

Тем не менее, существует довольно широко используемый шаблон, в котором вы перегружаете оператор глобально, но при этом вызываете функцию-член:

class whatever { 
    // make this public, or the global overload a friend.
    std::ostream &write(std::ostream &dest) const { 
        // write self to dest
    }
};

std::ostream &operator<<(std::ostream &os, whatever const &w) { 
     return w.write(os);
}

Это особенно полезно, когда / если вы хотите полиморфное поведение. Вы не можете сделать перегруженный оператор самим полиморфным, но вы делаете функцию-член, которую он вызывает virtual, таким образом, она все равно действует полиморфно.

Редактировать: чтобы (надеюсь) прояснить ситуацию, вы можете сделать это несколькими различными способами. Первое и, вероятно, наиболее очевидное - это сделать общедоступным наш элемент write и сделать так, чтобы глобальный оператор вызвал его. Поскольку это общедоступно, нам не нужно делать ничего особенного, чтобы позволить оператору использовать его:

class myClass {
public:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }   
};

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
   // since `write` is public, we can call it without any problem.
   return m.write(os);
}

Вторая альтернатива - сделать write приватным и объявить operator<< другом, чтобы предоставить ему доступ:

class myClass {
    // Note this is private:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }

    // since `write` is private, we declare `operator<<` a friend to give it access:
    friend std::ostream &operator<<(std::ostream &, myClass const &);
};

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
   return m.write(os);
}

Третья возможность почти такая же, как и вторая:

class myClass {
    // Note this is private:
    std::ostream &write(std::ostream &os) const { 
        // write stuff to stream
        return os;
    }

    // since `write` is private, we declare `operator<<` a friend to give it access.
    // We also implement it right here inside the class definition though:
    friend std::ostream &operator<<(std::ostream &os, myClas const &m) { 
        return m.write(os);
    }
};

В этом третьем случае в C ++ используется довольно странное (и малоизвестное) правило, называемое «внедрение имени». Компилятор знает, что функция friend не может быть частью класса, поэтому вместо определения функции-члена это «внедряет» имя этой функции в окружающую область (в данном случае, глобальную область). Даже если operator<< определено внутри определения класса, это не функция-член вообще - это глобальная функция.

10 голосов
/ 22 марта 2012

Вы можете перегрузить operator<< как функцию-член. Но вы не можете написать члена operator<<, который принимает ostream слева, а ваш класс - справа.

Когда вы делаете что-то (нестатическую) функцию-член, есть подразумеваемый первый аргумент, вызывающий объект. operator<< является двоичным, поэтому он принимает только 2 аргумента. Если вы сделаете его функцией-членом, вы можете дать ему только один параметр, потому что он уже есть (вызывающий объект). И поскольку этот вызывающий объект всегда является первым аргументом, вы не можете написать оператор вывода в качестве (нестатического) члена (по крайней мере, не в стандартной форме для этой конкретной функции), потому что в этом случае ostream должен быть первым аргументом.

1 голос
/ 22 марта 2012

Думайте об этом так: когда вы хотите выполнить потоковую передачу в ostream, вы вызываете << оператор <em>для объекта потока . И вы не можете напрямую изменять приватный метод ostream. Поэтому вам нужно создать перегруженную версию и сделать ее другом.

Когда вы создаете свой собственный метод operator << в своем классе, вы создаете метод <<, который будет работать с вашим классом, а не с объектом ostream. Я полагаю, что вы можете хранить ostream внутри своего класса и транслировать его, но у вас будут трудности с написанием цепочек утверждений, таких как: <code>a << b << c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...