Overloading the stream input operator (<<) for a class
Can I overload it as a member function?
Let's say if you have a class Foo
and want to use:
Foo foo; std::cout << foo;
No he can not.
Member function overloading only works if the first argument is a class object. In the case of stream insert statements, the first argument is a stream, not a class object.
If you want to use:
Foo foo; foo << std::cout;
Yes, it can be defined as a member function.
Non-member function and virtual member function can be combined with good effect when used in a situation where derived classes are involved.
struct Shape { // Member function that can be overridden by derived classes. virtual std::ostream& operator<<(std::ostream& os) const = 0; }; // Non-member function that makes use of member functions. std::ostream& operator<<(std::ostream& os, Shape const& shape) { return shape << os; } struct Rectangle : public Shape { virtual std::ostream& operator<<(std::ostream& os) const { // Do the need full to stream a Rectangle. // ... return os; } }; struct Ellipse : public Shape { virtual std::ostream& operator<<(std::ostream& os) const { // Do the need full to stream an Ellipse. // ... return os; } };
Using:
Rectangle r; Ellipse e; std::cout << r << std::endl; std::cout << e << std::endl;
source share
Can I overload it as a member function?
Not. The function signature disables this option.
// Binary operator where the stream object is left of '<<' and the object // instance is right of '<<' std::ostream& operator<<(std::ostream& lhs, const Foo& rhs)
For illustration, here's the binary operator + as a free function:
class Foo { }; Foo operator+(const Foo& lhs, const Foo& rhs) { // an appropriate implementation } int main() { Foo f1; Foo f2; // Uses the free function Foo result = f1 + f2; return 0; }
However, implemented as a member function, it looks like this:
class Foo { public: Foo operator+(const Foo& other) const { // an appropriate implementation } }; int main() { Foo f1; Foo f2; // The calling sequence is the same as before however 'f1' is logically // the same as 'lhs' in the free function implementation example (i.e., // the first type in the binary operator must be the class type when // implemented as a member function) Foo result = f1 + f2; return 0; }
Given the operator+
member function example , it is easier to see why a free function std::ostream& operator<<(std::ostream& lhs, const Foo& rhs)
cannot be converted to an equivalent member function. Since "lhs" must be of the same type as std::ostream
, but this is only possible by implementing this class, whereas this example implements the class Foo
.
source share
If you want to be strong enough, you can use member function overloading, but this is directly against the convention, so it would lead to confusion. This will also prevent string fetch / insert operations and will not work for reading / writing primitive types.
The reason for this is that calling an operator overloaded as a member function means that it is x << y;
interpreted as x.operator<<(y);
, so the overloaded operator must be a member of the left operand, not the right. It is possible (iostreams include such overloads for some types), but essentially cannot be extended (all such overloads must be part of the iostream object when it is the left operand).
If you want to overload it as a member of an object to be read / written, you need to put the object read / written as the left operand. This means that the insert will go from left to right, not right to left. To have any chance of preserving sanity, you will almost certainly want to use >>
for insert and <<
for extract:
class my_class { int x; public: bool operator<<(std::istream &is) { // Note the reversal here. That because we're defining this // for a `my_class` on the left, and an iostream on the right, // but the existing `>>` and `<<` use the reverse of that (iostream // on the left). is >> x; return is.good(); } bool operator>>(std::ostream &os) { os << x; return os.good(); } }; my_class a; a >> std::cout; a << std::cin;
Note. I am providing this answer only to indicate that it is technically possible to do this and to show how to do it. If you're starting with a clean slate and you're really tuned into using member functions, I can see where you could possibly choose this method - but even then it can raise a lot of questions.
In particular (as noted above) this will not support chaining. Since insertion and extraction are performed as member functions of the left operand, this also means that it will only work for user-defined types, not primitive types (int, short, long, float, double, bool, etc.)
Bottom line: If you want strong enough, you can make it work to a limited extent under limited circumstances - but neither well (no chaining) nor ubiquitous (no primitive types).
source share