Returning OS.str() is guaranteed to copy the underlying string, whereas OS.flush(); return Underlying; makes Underlying a candidate for NRVO, or at worst, implicit move.
To keep this kind of inefficiency at bay in the future, the fast code should probably be made easier to type than the slow code (as it's currently the opposite). Perhaps this could be solved by:
- making raw_string_ostream guarantee+document that it does not need to be flush()ed (similar to raw_svector_ostream), and/or
- making raw_string_ostream::str() return a StringRef rather than std::string&, to discourage using it to initialize std::strings
Implementing those two ideas would make simply return Underlying; the natural, correct, and efficient choice. They are, however, out of scope for this patch.
FWIW, it appears to me that in most (all?) of these cases, what's really wanted is not "a string and a stream" but rather "a stream that owns a string" (std::ostringstream or the LLVM-codebase equivalent thereof). Then the return can be return std::move(OS).str(); — for std::ostringstream, this Does The Right Thing since C++20, and if LLVM had its own stringstream it could make it Do The Right Thing today.
https://en.cppreference.com/w/cpp/io/basic_ostringstream/str