Why can't std::ostream be moved?
Originally they were movable. This turned out to be a design flaw on my part, and discovered by Alberto Ganesh Barbati:
http://cplusplus.github.io/LWG/lwg-defects.html#911
The issue shows a few examples where ostream
gets moved and/or swapped, and the results are surprising, instead of expected. I was convinced that these types should not be publicly movable nor swappable by this issue.
How can an std::ostream be moved?
You've almost got it right. Your example is move constructing the ios
base twice. You should move only the direct base class. And assuming there is member streambuf
, move that too:
class omstream
: public std::ostream {
// suitable members
public:
omstream(/* suitable constructor arguments */);
omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
: std: ostream(std::move(other)),
// move any members {
this->set_rdbuf(/* install the stream buffer */);
}
// other helpful or necessary members
};
I changed "get" to "install" in the set_rdbuf
comment. Typically this installs a pointer to the member streambuf
into the ios
base class.
The current unorthodox design of the move and swap members of istream/ostream
was set up to make the move and swap members of the derived classes (such as ofstream
and omstream
) more intuitive. The recipe is:
Move the base and members, and in the move constructor set the
rdbuf
.
It is that embedded rdbuf
that is the complicating factor for the entire hierarchy.
Why can't I move std::ofstream?
According to the standard
27.9.1.11 basic_ofstream constructors
or, its more "readable" version http://en.cppreference.com/w/cpp/io/basic_ofstream/basic_ofstream , std::basic_ostream<>
has a move constructor, so the code should compile.
clang++ 3.5 compiles it with -std=c++11
or -std=c++1y
. Also gcc5 compiles it, so probably it is not implemented in libstdc++ for gcc < 5
Interestingly, the lack of move semantics is not mentioned on gcc's stdlibc++ implementation https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2014
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 for a bug report, thanks to @BoBTFish for pointing out. It is confirmed that the issue was fixed in gcc5.
Weird behaviour when holding std::ostream rvalue member
The temporary std::ofstream{"testic"}
that you created only exists for the duration of the constructor call. After that it is destroyed and the file is closed, which means you are left with a reference that refers to garbage. Using that reference results in undefined behavior.
To fix it you can remove the reference all together (the &&
from std::ostream&& out
and std::ostream&& o
) and have it create a new object that is initialized from the temporary.
The above won't work because std::ostream
cannot be moved. You will have to use a pointer instead if you want to maintain polymorphism. If that isn't important you can change all std::ostream&&
to std::ofstream
:
class A {
private:
std::unique_ptr<std::ostream> out;
public:
A(std::unique_ptr<std::ostream> o) : out(std::move(o)) {
out->write("test", 4);
}
void writeTest2() {
out->write("test2", 5);
out->flush();
}
};
int main() {
A a{std::make_unique<std::ofstream>("testic")};
a.writeTest2();
}
Can't we manage std::mapstring,ofstream?
As an std::ostream
is not copyable (copy constructor and assignment operator are marked deleted), you have to either construct the ofstream directly in the map (e.g. using std::map::emplace()
) or use move assignment.
Construct in-place
There are basically two ways, either default-construct stream in the map (pre C++11), or call std::map::emplace()
to supply ofstream
constructor arguments.
Using default-construction (works even pre C++11):
map<string,ofstream> m;
// default-construct stream in map
ofstream& strm = m["test"];
strm.open("test_output");
strm << "foo";
Using emplacement:
// 1st parameter is map key, 2nd parameter is ofstream constructor parameter
auto res = m.emplace("test", "test_output");
auto& strm = res.first->second;
strm << "bar";
Move assignment
We can construct the stream outside of the map first, turn it into an rvalue by calling std::move()
and use move assignment operator to move it into the map:
map<string,ofstream> m;
ofstream strm("test_output");
m["test"] = std::move( strm );
// strm is not "valid" anymore, it has been moved into the map
We can even get rid of std::move()
if we directly create the stream as an rvalue:
m["test"] = ofstream("test_output");
Move assignment is less efficient than the other methods, because first a stream will be default-constructed in the map, just to be replaced by the move-assigned stream then.
Live demo of all three methods.
Note: Sample code omitts any error handling for brevity. State of stream should be checked after opening and after each stream operation.
Why the constructor of std::ostream is protected?
I'll admit that I don't understand it either. I can't find any
default constructor at all for std::istream
, and I would think
you would want one if you want to create a bidirectional stream,
because of the strange way std::ios_base
works: the
constructor does not initialize anything, but the derived
class must call std::ios_base::init
explicitly in its
constructor. When multiple inheritance is involved (i.e.
bidirectional IO, where the class derives from both std::istream
and std::ostream
), I would expect only the most
derived class to call std::ios_base::init
. (Instd::iostream
, std::ios_base::init
will be called twice.)
In fact, before looking it up in the standard, I was about to
answer that the default constructor was protected, because it
didn't call std::ios_base::init
, and using it directly, rather
than in a derived class, would result in an uninitialized
stream.
Anyhow, your immediate problem has a simple solution:
std::ostream out( NULL );
Also: the function you need to set up its sink later is the
non-const version of rdbuf()
, not copyfmt()
. rdbuf()
is
used to read and to set the pointer to the streambuf
,copyfmt()
copies the formatting flags, but does not touch
the pointer to streambuf
.
So you can do things like:
std::ostream out( NULL );
// ...
std::filebuf fileBuffer;
if ( filenameGiven ) {
fileBuffer.open( filename.c_str(), std::ios_base::out );
}
if ( fileIsOpen() ) {
out.rdbuf( &fileBuffer );
} else {
out.rdbuf( std::cout.rdbuf() );
}
(I do this a lot. In fact, I thought that it was the usual
idiom when you didn't know up front whether to output to a file
or to std::cout
.)
EDIT:
And yet another correction: the non-const version of rdbuf
calls clear()
,
so you don't have to. (I knew I'd done this without calling clear()
, but
when I saw that init
set badbit
...)
Anyhow: the summary is: it's usually preferrable to pass a pointer to a valid
streambuf to the constructor of std::ostream
, but if you can't, it's
perfectly valid to pass a null pointer, and set a valid pointer later usingrdbuf()
. And the answers which say otherwise are simply wrong.
Why does std::ostream not compile when used in ternary operator?
The standard contains some complicated rules regarding how the conditional expression is evaluated ([expr.cond]). But instead of quoting those rules here, I'm going to explain how you should think about them.
The result of the conditional expression can be an lvalue, xvalue, or prvalue. But which one of these it is has to be known at compile time. (The value category of an expression can never depend on what happens at runtime). It's easy to see that if both the second and third expressions are lvalues of the same type, then the result can also be made an lvalue, and no copying has to occur. If both the second and third expressions are prvalues of the same type, then, as of C++17, no copying has to occur either---a prvalue of type T
represents the deferred initialization of an object of type T
, and the compiler simply chooses, based on the condition, which of those two prvalues gets passed on to eventually be used to initialize an object.
But when one expression is an lvalue and the other is a prvalue of the same type, then the result must be a prvalue. If the standard said the result would be an lvalue, that would be illogical, as the condition may cause the prvalue operand to be selected, and you can't convert a prvalue into an lvalue. But you can do it the other way around. So the standard says that when one operand is an lvalue and the other is a prvalue of the same type, then the lvalue must undergo the lvalue-to-rvalue conversion. And if you attempt an lvalue-to-rvalue conversion on an std::ostream
object, the program will be ill-formed since the copy constructor is deleted.
Thus:
- In A, both operands are prvalues so there is no lvalue-to-rvalue conversion; this is ok in C++17 (but not in C++14).
- In B, the lvalue-to-rvalue conversion is needed for
o
, so this won't compile. - In C,
oRef
is an lvalue so the lvalue-to-rvalue conversion is still required, so this also won't compile. - In D,
oRRef
is still an lvalue (as the name of an rvalue reference is an lvalue). - In E, one argument is a prvalue and one is an xvalue. The xvalue still needs to be converted into a prvalue to make the result a prvalue.
The case of E deserves some further remarks. In C++11 it was clear that if one argument is an xvalue and the other is a prvalue of the same type, the xvalue must undergo the (misleadingly named) lvalue-to-rvalue conversion to yield a prvalue. In the case of std::ostream
, this uses the protected move constructor (so the program violates member access control). In C++17, one could contemplate changing the rule so that instead of the xvalue being converted to a prvalue, the prvalue gets materialized to yield an xvalue instead, obviating the need for a move. But this change has no obvious benefit and it's questionable whether it's the most reasonable behaviour, so that's probably why it wasn't made (if it were even considered).
How to move std::ostringstream's underlying string object?
The standard says that std::ostringstream::str()
returns a copy.
One way to avoid this copy is to implement another std::streambuf
derived-class that exposes the string buffer directly. Boost.IOStreams makes this pretty trivial:
#include <boost/iostreams/stream_buffer.hpp>
#include <iostream>
#include <string>
namespace io = boost::iostreams;
struct StringSink
{
std::string string;
using char_type = char;
using category = io::sink_tag;
std::streamsize write(char const* s, std::streamsize n) {
string.append(s, n);
return n;
}
};
template<typename T>
std::string ToString(T const& obj) {
io::stream_buffer<StringSink> buffer{{}};
std::ostream stream(&buffer);
stream << obj;
stream.flush();
return std::move(buffer->string); // <--- Access the string buffer directly here and move it.
}
int main() {
std::cout << ToString(3.14) << '\n';
}
Related Topics
How to Pass Derived Classes by Reference to a Function Taking Base Class as a Parameter
Does Delete Work with Pointers to Base Class
Shared_Ptr<> Is to Weak_Ptr<> as Unique_Ptr<> Is To... What
How to Use Const in Vectors to Allow Adding Elements, But Not Modifications to the Already Added
Error C2065: 'Cout':Undeclared Identifier
How to Increment an Iterator by Just Adding a Number
Understanding Region of Interest in Opencv 2.4
When to Use Functors Over Lambdas
Error: Class Has Not Been Declared Despite Header Inclusion, and the Code Compiling Fine Elsewhere
Why Does the C++ Standard Algorithm "Count" Return a Difference_Type Instead of Size_T
Uses for Anonymous Namespaces in Header Files
What Should a C++ Getter Return
What Is Assignment via Curly Braces Called? and Can It Be Controlled
Initialize Global Array of Function Pointers at Either Compile-Time, or Run-Time Before Main()