Why the constructor of std::ostream is protected?

user2485710 picture user2485710 · Aug 3, 2013 · Viewed 18k times · Source

Why I can't just create an "empty" stream for my output like this

std::ostream out;

?

This rows is apparently illegal with both clang 3.4 and gcc 4.8.1 under linux with libstdc++, I really don't get why, I mean why I can't just create a stream out of nowhere and use it like I want to ? notice that std::ofstream out; is 100% ok instead. I just don't get the logic behind this. This is even stranger if you consider that after the creation I can just use this buffer and share a common buffer with other buffers with copyfmt so there is no real need for my std::ostream to be initialized to something right from the creation of the object to be useful.

Please don't deviate from this, I don't need stringstreams, I need ios streams because of what I have to do and because of the methods and the properties that they are offering.

Answer

James Kanze picture James Kanze · Aug 3, 2013

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. (In std::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 using rdbuf(). And the answers which say otherwise are simply wrong.