I try to overload operator<< for my class so that it print member when I do
std::cout << obj;
I see that the way to do this is
std::ostream& operator<<(std::ostream& os, const T& obj)
{
// write obj to stream
return os;
}
What are the basic rules and idioms for operator overloading?
However, I try to make my code conforms to Google C++ style guide
https://google.github.io/styleguide/cppguide.html#Reference_Arguments
It says that passing the reference without const is not allowed except for the case that it is needed by convention such as swap(). Is this overloading operator<< in the same category as swap()? or there is a way to do something like
std::ostream& operator<<(std::ostream* os, const T& obj)
^
? or something that does not take non-const reference as the input.
If so, please teach me how to do that. Thank you.
It says that passing the reference without const is not allowed except for the case that it is needed by convention
Well, the stream is conventionally passed as a non-const reference into the stream insertion and extraction operators, so it would appear that the rule has allowed an exception for you. As such, defining the suggested overload should be conforming to the rule despite accepting a non-const reference argument.
That said, I'm not an authority on what Google would consider as convention. If you work for Google, you should know whom to ask; If you don't then you don't need to get stressed over their style.
Related
Suppose we had an istream subclass with the following method:
SomeStream& operator>>(Something& something) {
// .. write data into something
}
And we also had the following global method:
Something& operator>>(istream& stream, Something& something) {
// .. write data from stream into something
}
How does C++ know which method to call for the following code:
instanceOfSomeStream >> instanceOfSomething;
Follow up question: what is the better or more common way to allow a SomeStream to be written into a Something - should I add another overload for operator>> in SomeStream, that takes a Something& argument? Or should I go the opposite way, and create an overload in Something that takes a SomeStream? (Actually not in Something, but as a global function because of obvious reasons).
C++ has complicated overload resolution rules in general. In this case it's useful to know that a member function, for the purposes of overload resolution, behaves like a non-member function that takes a reference to the class as an implied first argument, i.e., the choice is between something like [1]
SomeStream& operator>>(SomeStream& somestream, Something& something)
and
Something& operator>>(istream& stream, Something& something)
Of course the return type doesn't matter, and for
instanceOfSomeStream >> instanceOfSomething
the first overload will be chosen because SomeStream is more derived than istream.
Your follow-up question should be a separate question. There's some discussion here which might be related: Should operator<< be implemented as a friend or as a member function?
[1] Note: I say "something like" because this is not exactly correct. For example such a member function can be called on an rvalue object expression even though an lvalue reference is shown above.
std::ostream & _return = ::operator<<(_os, _event)
Especially I would like to know: What is the data type of _return and How can I print it in console.
std::ostream & _return = ::operator<<(_os, _event);
Especially I would like to know: What is the data type of _return and How can I print it in console.
The code looks for an operator<< at global scope (not in any namespace), which can accept the _os and _event objects as parameters. It's not necessarily true, but given "<<" is the normal way streaming output is done in C++, you can expect that it's probably going to be a function such as:
std::ostream& operator<<(std::ostream&, const Event&);
Where Event is whatever type the _event object has, and I've assumed _os will be some type derived from std::ostream, and consequently able to be handled by a reference to std::ostream.
Almost all such operator<< functions return their first stream argument, so your code is probably doing some output, then effectively assigning to _return as if it were:
std::ostream& _return = _os;
Here, the static type of _return itself is std::ostream& - a reference to a std::ostream (Standard output stream) object, but the run-time type will be whatever type _os has... that's the type of object that operations on _return will invoke polymorphically. This could be ofstream, ostringstream or many other types.
How can I print it in console.
There is no Standard way to get textual type names in C++, though runtime polymorphic types do have runtime type information that includes an optional textual field that's normally populated with a type name of some sort - whether it will have full namespace qualifiers, name mangling or whatever is unspecified, but you can try it easily enough:
std::cout << typeid(_return).name() << '\n';
(For GCC, see Unmangling the result of std::type_info::name for tips on unmangling such type names)
_return is just a variable of type "Reference to std::ostream class object".
And it is initialized with a return value of << operator in global namespace ::operator<<(ostream& os, const some_Obj_reference& ref).
Ttis could be as well
std::ostream & _return = (_os <<_event);
From cplusplus.com, I saw that ostream class's member function operator<< looks like this:
ostream& operator<< (bool val); ostream& operator<< (int val);
.... and so on.
It does make sense because when you use the Cout object like cout<<x you activate the ostream& operator<< (int val) function, so you actually use the << operator on Cout object. This is very much like every other operator and sends the int variable to the function. What is the difference and what exactly happens when I want to stream an object of my own? Why does the syntax is suddenly ostream& operator<< (**ostream &os**, object ob)?
Why do I need to add the ostream var? I am still using cout<<ob so whay isnt it just ostream& operator<< (object obj)? All I pass is my object. The cout object is allready there.
operator<< is generally defined as a free function; that is, not a member function. Since it is an overloaded binary operator, that means it get's its left argument first and its right argument second.
The operator<< traditionally returns a reference to its left argument to enable the idiomatic chain of output.
To make it obvious to the reader, I tend to define my operator overloads using the lhs and rhs abbreviations; an operator<< would look similar to this, for some type T.
std::ostream& operator<<(std::ostream& lhs, T const& rhs)
{
// TODO: Do something
return lhs;
}
As a member function
As with other binary it could be defined as a member function. That is, let us suppose that you with defining your own iostream. Amongst other things, your class declaration may look like this. Again, T is a particular type.
class MyIOStream : public iostream
{
public:
MyIOStream& operator<<(T const& rhs);
}
In this case operator<< is a member function. It has the same semantics when used as <<.
References
Operators in C and C++ - a great summary of all the operators you can overload and their typical arguments.
why do I need to add the ostream var?
I'm sure it's there so that you can chain outputs together:
cout << foo << bar
The first call, cout << foo will result in an ostream reference that can be used for the << bar part.
Some of the stream extractors are members of basic_istream; because they are members, the basic_istream argument is implied. Some of the stream extractors are not members of basic_istream. Because they are not members, the basic_istream argument has to be part of the declaration.
Like this (oversimplified):
class basic_istream {
public:
basic_istream& operator>>(int& i);
}
basic_istream& operator>>(basic_istream&, std::string& str);
Both can be called in the same way:
int i;
std::cin >> i; // calls basic_istream::operator>>(int&)
std::string str;
std::cin >> str; // calls operator>>(basic_istrea&, std::string&)
I have a log builder type like so:
Log Log::log(const int logLevel)
{
return Log(logLevel);
}
Log& operator <<(Log& log, const char * s)
{
if (log.hasLogLevel())
log.out << s;
return log;
}
I'm using the above code like this:
Log::log(1) << "Hello logger";
But I'm getting these warnings and it wasn't until recently I realized that it's because the way the operator is overloaded (or at least this is what I'm thinking)
warning C4239: nonstandard extension used : 'argument' : conversion from 'snow::Log' to 'snow::Log &'
I thought this would be fine because it's the same rvalue? that's being passed/chained through these operator overloads. I don't think this code compiles outside of MSVC++ and I would like to know what I should be doing differently here.
If the solution is to simply use rvalue references then I'm cool with that but I'd like to understand a little better what's going on here.
The problem is that the Log is an rvalue, and that is not allowed to bind to the non-const reference parameter. Microsoft doesn't enforce this, bacuase they have some legacy code that would break.
If you only want to output strings, one workaround is to make the operator<< a member of the Log class. You are allowed to call members of an rvalue.
If you want to use other non-member operators, you can provide an rvalue to lvalue converter, like the standard streams do in C++11.
Something like
template<class T>
Log& operator<<(Log&& log, const T& value)
{ return log << value; }
using the fact that inside the operator log is an lvalue and can bind to a non-const reference of the other operators.
I have a section of code that seems to have a recursive warning when I compile, any ideas why?
ostream& operator << (ostream& out, const node& rhs)
{
out << rhs.get_data();
return out;
}
It is calling this function:
node::value_type node::get_data() const
{
return data;
}
This is just a guess, since you haven't posted a self-contained example. In particular, the definition of node would be very useful.
I think that, for some reason, the compiler is choosing to convert rhs.get_data() into a node, probably using an implicit conversion constructor, rather than selecting an overload of operator<< that takes node::value_type. You should:
Make sure that operator << (ostream&, node::value_type) has been declared before your definition of operator<<
If node has a constructor that takes value_type, then it's probably best to make it explicit to avoid unexpected implicit conversions.