Why doesn't this << overload compile
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 <io开发者_开发技巧stream>
#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;
}
It's not a restriction that any operator must be "friend
ed". 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.
Because the first form overloads temp << std::cout
.
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.
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.
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.)
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.
精彩评论