Why are iostreams not copyable?

Copying and moving are value-semantic operations. To define them, you first have to decide what properties of a class give its objects distinct values. This point was at first largely sidestepped for the iostreams library, and then C++11 took a different direction incompatible with such a copy constructor.

The state of a stream object comprises two parts: A pointer to a stream buffer with its associated state, and formatting information. Since C++98, rdbuf, rdstate, and copyfmt expose this information separately.

Since C++11, stream classes also have a protected interface including a move constructor (and a member called move) which copies the format but not the stream buffer pointer. This commits iostream to treating the formatting information exclusively as the state of the stream object.

If streams were made copyable at this point, it would only do copyfmt and not the rest.

The choice to exclude rdbuf from the value state may be due to the further-muddled value semantics of derived classes such as std::fstream, which not only expose access to a stream buffer, but also embed and own it.

std::ifstream f( path + filename ); // Owns, or even "is," a file.
std::istream i = f; // Observes an externally-managed file.

std::istream i2 = i; // OK, copy a shallow reference.
std::ifstream f2 = f; // Error, ifstream is more than a shallow reference.

std::istream i3 = std::move( f ); // Error? Would retain a reference to an rvalue.
std::ifstream f3 = std::move( f ); // OK: full copy including the file buffer.

The semantics could be consistent in some fashion, but it would be a lot of confusion for a moderate gain.

Leave a Comment