views:

267

answers:

6

I can't figure out why the following code doesn't compile. The syntax is the same as my other operator overloads. Is there a restriction that the << overload must be friended? If so, why? Thanks for any help.

This doesn't work -

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

class Test
{
 public:
explicit Test(int var):
    m_Var(var)
    {   }

    std::ostream& operator<< (std::ostream& stream)
    {
        return stream << m_Var;
    }
 private:
int m_Var;

 };

 int _tmain(int argc, _TCHAR* argv[])
 {
Test temp(5);

std::cout << temp;

return 0;
}

This does work -

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

class Test
{
public:
explicit Test(int var):
    m_Var(var)
    {   }

    friend std::ostream& operator<< (std::ostream& stream, Test& temp);

private:
    int m_Var;

 };

 std::ostream& operator<< (std::ostream& stream, Test& temp)
 {
return stream << temp.m_Var;
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
Test temp(5);

std::cout << temp;

return 0;
 }
+8  A: 

Because the first form overloads temp << std::cout.

KennyTM
+8  A: 

It's not a restriction that any operator must be "friended". The problem is that, if you declare an operator as an instance method, the first argument is always forced to be the type of the class itself. In this case, you need the first parameter of the operator (left hand side) to be of type std::ostream& therefore, you can't use an instance method to overload it and will have to use a global function.

By the way, it's not at all required that operators declared as separate functions to be declared as friend functions too. They can happily work without being a friend of the class as long as they only access public members of their arguments.

Mehrdad Afshari
I know that about the other operators, I wasn't aware that there were special rules for the streaming operators.
Steve
@Steve: Nothing is special about `<<` and `>>`. They are fundamentally bitwise shift operators overloaded for stream classes in the C++ library.
Mehrdad Afshari
+4  A: 

When implemented as a member function, operator overloads have an implicit first parameter that becomes this. For streams this is out of order: the stream must come first.

Using a friend operator is short, concise, and can prevent undesired implicit conversions (due to only being used through ADL). If you want to define it out-of-line (e.g. in an implementation .cpp file), then have it call a non-public and possibly virtual method:

struct T {
  template<class Ch, class Tr>
  friend
  std::basic_ostream<Ch, Tr>& operator<<(
    std::basic_ostream<Ch, Tr>& s,
    T const& v
  ) {
    s << v.stuff; // or call v.output(s), etc.
    return s;
  }

private:
  int stuff = 0; // initialization here is c++0x only

  //virtual void output(std::ostream&) const;
  //virtual void output(std::wostream&) const;
  // etc., or make it a template, but if it's short,
  // just put it in the above friend overload
};

Bonus points: name the operator overload members that don't have this. (Hint: They're static.)

Roger Pate
A: 

As summarised by Scott Meyers in Effective C++ 2nd Edition Item 19: Differentiate among member functions, non-member functions, and friend functions:

operator>> and operator<< are never members. If f is operator>> or operator<<, make f a non-member function. If, in addition, f needs access to non-public members of C, make f a friend of C

This statement is just a guideline for deciding whether to make operator<< and operator>> members. It is best to make them non-members. You can make them members if you like but if you did, you would be forced to write:

temp << std::cout // when calling operator<<
temp >> std::cin  // when calling operator>>

You could actually correct your first piece of code by changing the call to std::cout to the above form. But this way of writing is definitely not natural.

Concerning friending for the operators (as non-members), if either operator needs access to private/protected data members (as is your case) then it must be a friend as it is external to the class.

Yukiko
well, this is somewhat misleading. They're not member function because its disallowed, not because its a bad idea. I have to friend them to get around it.
Steve
Not going to d/v, but this doesn't answer the question "*why* must op<< be a friend"
John Dibling
The other answers are perhaps clearer... but I added some clarification for what it is worth ;)
Yukiko
+4  A: 

When you are doing std::cout << temp; this mean you are applying << operator to std::cout (since operators are left associative). If you want to write an operator which is a member function to achieve this, you would have to overload << operator to whatever class std::cout belongs to, which is not possible since that is something you cant modify.

So you have to write a function which enables that, one way is overload << in global namespace which takes two arguments , the stream as well as the object which you want to display to console. something like this

std::ostream& operator<< (std::ostream& stream, Test& temp)

Now you could either make it friend or not. If you don't make it a friend you would have to provide getter functions (like getMVar )which would provide you the value of members Test class. However this is not a good approach. Since this you would unnecessarily need to provide getter functions. So it is generally a convention to make such operators friends.

As pointed out earlier , what you are doing will make lead to code being written as this temp << std::cout which is clearly not what you want.

Yogesh Arora
+5  A: 

Here's the fundamental reason why the stream operators have to be friends.

Take this code:

   struct Gizmo
    {
        ostream& operator<<(ostream& os) const
        {
            os << 42;
        }
    };


    int main()
    {
        Gizmo g;
        cout << g;
        return 0;
    }

Consider the context of the call to cout << g; When the compiler compiles this function, it first tries this:

cout.operator<<(g);

...and if that isn't found, then it looks in the global namespace for:

operator<<(cout, g);

...and if that isn't found, then it can't be compiled.

But when you try to implement the stream insertion operator as a member of Gizmo, you are hoping the compiler will resolve your code to:

g.operator<<(cout);

...which it can't do unless you change your code to:

g << cout;

...which is obviously not what you're going for.

John Dibling