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.