1 回答
basic_ostream
has an overload of operator<<
that looks like this:
template <typename Elem, typename Traits, typename T>
basic_ostream<Elem, Traits>&
operator<<(basic_ostream<Elem, Traits>&& sink, const T& val)
{
return sink << val;
}
This is called "Rvalue stream insertion" in the standard, at §27.7.3.9 [ostream.rvalue].
It allows implicit conversion (of sorts) from an rvalue basic_ostream
to an lvalue. It was introduced specifically to allow temporary streams to be usable without resorting to tricks.
As for why the compile fails when you omit the move:
When Stream& operator<<(Stream& s, Dummy)
is called without the move, Stream
will be std::fstream
which inherits from std::ostream
(i.e. basic_ostream<char>
).
It will use the basic_ostream<E, T>& operator<<(basic_ostream<E, T>&, const char*)
overload to insert your string, then try to return the result of that expression which will be an ostream
. You cannot implicitly downcast from std::ostream&
to std::fstream&
, so you get an error.
You can fix this by returning s
on it's own line (where it won't have been implicitly upcasted.)
This isn't a problem with move because you go through that rvalue-to-lvalue insertion operator we just discovered first. Inside that function, the stream is a basic_ostream
and so Stream
is as well, and the return types will match.