28

I am trying to overload operator<< as a member function. It works if simply do this:

friend ostream& operator<<(ostream& os, const MyClass& myClass); in my header file and in my MyClass.cc file:

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

However, if I try to take the friend off and make it a member function, then it complains that operator<< can only take one argument. Why?

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

I read at this question that it can't be a member function, but not sure why?

Community
  • 1
  • 1

3 Answers3

62

When overloaded as a member function, a << b is interpreted as a.operator<<(b), so it only takes one explicit parameter (with this as a hidden parameter).

Since this requires that the overload be part of the class used as the left-hand operand, it's not useful with normal ostreams and such. It would require that your overload be part of the ostream class, not part of your class. Since you're not allowed to modify ostream, you can't do that. That leaves only the global overload as an alternative.

There is, however, a fairly widely-used pattern where you overload the operator globally, but have that call a member function:

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);
}

This is particularly useful when/if you want polymorphic behavior. You can't make the overloaded operator polymorphic itself, but you make the member function it calls virtual, so it acts polymorphic anyway.

Edit: to (I hope) clarify the situation, you can do this a few different ways. The first and probably most obvious is to just make our write member public, and have the global operator call it. Since it is public, we don't have to do anything special to let the operator use it:

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);
}

A second alternative is to make write private, and declare operator<< a friend to give it access:

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);
}

There's a third possibility that's almost like the second:

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);
    }
};

This third case uses a rather strange (and little known) rule in C++ called "name injection". The compiler knows that a friend function can't be part of the class, so instead of defining a member function, this "injects" the name of that function into the surrounding scope (the global scope, in this case). Even though operator<< is defined inside the class definition, it's not a member function at all -- it's a global function.

iksemyonov
  • 4,106
  • 1
  • 22
  • 42
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • what if the `operator<<` is declared as public in the class but not implemented in the class as `MyClass::operator<<`? Is `friend` required? –  Mar 21 '12 at 23:57
  • Interesting. So it can be declared in the class, implemented as a non-member function and not be a friend of the class to still call public member functions of that class. –  Mar 22 '12 at 00:04
  • @0A0D: I'm not quite sure I follow. In my previous comment, "it" referred to the member function. Are you talking about something else? – Jerry Coffin Mar 22 '12 at 00:06
  • 1. (In header) `class MyClass { public: ostream& operator<<(ostream&os, MyClass &myClass); }` 2. (In .cc file) `ostream& operator<<(ostream& os, MyClass &myClass) { return myClass.print(); }` So is operator<< required to be a `friend`? –  Mar 22 '12 at 00:09
  • Hmm, but if I do just what I showed and declare it as a friend it works and print is public. –  Mar 22 '12 at 00:17
  • I see the third example. Is there anything wrong with using friend or is it useless? –  Mar 22 '12 at 01:11
  • The third version is the one I prefer, if that's what you're asking. – Jerry Coffin Mar 22 '12 at 01:14
  • Thanks for your input. I used the third version in some code on our baseline. Using the **non-friend** version is what came out in a code review by a reviewer. He suggested I not use **friend**. So if there is no performance decrease or efficiency lost, then it must be left up to the programmer to decide. –  Mar 22 '12 at 01:39
  • There will normally be no difference in run-time efficiency at all. – Jerry Coffin Mar 22 '12 at 01:58
  • I discovered that in the first example, I have to declare it inline when it is outside the class because I have multiple files that are all linked together and the linker complains if I do not declare this function inline. –  Mar 22 '12 at 18:33
  • 1
    I was wondering: wouldn't it be better to template on the stream class? So that we can return exactly the stream type we were provided as input? Not sure about the pros, but it seems to me there's no cons. – Antonio Mar 21 '16 at 14:46
  • 1
    @Antonio: If you were designing things over again, you'd probably want to do that. With the current situation, it's close to pointless in most cases, simply because there are so many implementations that return `ostream &`, so anything that works over an open set of types has to account for that possibility anyway. OTOH, I'd have to agree that it's likely to be harmless. – Jerry Coffin Mar 21 '16 at 15:14
  • Might be worth pointing out that using the third option, that `operator<<` can only be found by Argument Dependent Lookup. The biggest actual effect being that it can't be called using a type which has a user-defined conversion to `myClass`. – aschepler Jun 25 '17 at 23:42
11

You can overload operator<< as a member function. But you can't write a member operator<< that takes an ostream on the left side, and your class on the right side.

When you make something a (non-static) member function, there is an implied first argument, the calling object. operator<< is binary, so it only takes 2 arguments. If you make it a member function, you can only give it one paremeter, because it already has one(the calling object). And since that calling object is always the first argument, you can't possibly write the output operator as a (non-static) member (at least, not in the standard form for that particular function), because for that case, the ostream needs to be the first argument.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 1
    Moreover, since the implicit `this` will be the first argument, you simply can't make ostream operators as class members--the order of the arguments will be backwards. – John Zwinck Mar 21 '12 at 23:38
  • @JohnZwinck although you could make your class do `myclass >> cout`, but that would be weird... especially with chaining. – Seth Carnegie Mar 21 '12 at 23:40
  • Does it have to be friended to access the print method in MyClass if it is a non-member function? –  Mar 21 '12 at 23:40
  • 1
    Not if the print method is public. Generally you shouldn't need to declare your ostream operators as friends, because they usually only print publicly-visible aspects of a class anyway. – John Zwinck Mar 21 '12 at 23:42
  • And if they are printing private data, then one has to wonder why that data is declared private. Because if I can print it, then I do in fact have access to it, though indirectly. – Benjamin Lindley Mar 21 '12 at 23:47
1

Think of it like this: when you want to stream to ostream, you're calling the << operator on the stream object. And you're not allowed to directly modify the ostream's 'private' method. So you have to create an overloaded version, and make it a friend.

When you create your own operator<< method in your class, you're creating a << method that will operate on your class, not an ostream object. I guess you can hold an ostream internally to your class and stream to it, but you'll have difficulty writing chained statements like this: a << b << c.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148