I was wondering how std::cout
is able to use <<
as it does.
My main puzzlement is with whether std::cout
as an instance of something. Basically, how is <<
defined? If I do this for a custom class, I need an instance of some sort...
I could see implementing it as kind of a hack with void pointers or something, but I'd like to see the actual way it's done.
Does anyone here know? Thanks
std::cout
is an instance of std::ostream
. std::cout << "something"
calls one of the operator<<
overloads as would be done for any instance of std::ostream
.
It's "special" in that it references the console, but otherwise it behaves exactly as an ofstream
or an ostringstream
would.
EDIT: Chaining works just like it works for any other operators. Consider:
class MyType
{
friend std::ostream& operator<<(std::ostream& target, const MyType& source);
int val;
public:
MyType()
: val(0)
{ }
MyType& Add(int toAdd)
{
val += toAdd;
return *this;
}
};
MyType& operator+(MyType& target, int toAdd)
{
return target.Add(toAdd);
}
std::ostream& operator<<(std::ostream& target, const MyType& source)
{
target << source.val;
return target; //Make chaining work
}
int main()
{
MyType value1;
value1 + 2 + 3 + 4;
std::cout << value1 << " and done!" << std::endl;
}
In this case, the chaining for +s on MyType
work for the same reason the <<
s work on top of std::ostream
. Both +
and <<
are left-associative, which means they're evaluated from left to right. In the case of overloaded operators, the operator is replaced with the equivalent function call.
EDIT2: In a bit more detail:
Let's say you're the compiler and you're parsing
std::cout << value1 << " and done!" << std::endl;
First, <<
is left associative, so you start on the left. You evaluate the first <<
and turn it into the function call:
operator<<(std::cout, value1) << " and done!" << std::endl;
You then see that you once again have a std::ostream
(the result of the call to operator<<
), and a char *
, which you once again turn into the function call:
operator<<(operator<<(std::cout, value1)," and done!") << std::endl;
and so on until you've processed the entire statement.