58

Clearly, streams can't be copied. It should be possible to move streams. According to 27.9.1.11 [ofstream.cons] paragraph 4 it is possible to move construct an std::ofstream (the same is true for std::ifstream, std::fstream, and the std::*stringstream variants). For example:

#include <iostream>
#include <fstream>
#include <string>

std::ofstream makeStream(std::string const& name) {
    return std::ofstream(name);
}

int main()
{
    std::ofstream out{ makeStream("example.log") };
}

Trying to move an std::ostream, e.g., to have a factory function creating an std::ofstream, an std::ostringstream, or some other stream according to a URN passed as argument doesn't work. std::ostream (well, the class template std::basic_ostream really) has a protected move constructor according to 27.7.3.1 [ostream].

Why can't std::ostream be moved itself?

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • What would you move the `std::ostream` to? You need to create an instance of the derived class, unless I'm misunderstanding you, and if you don't know the derived class at compile-time, how would the move work? –  Dec 25 '13 at 16:10
  • @hvd: I can certainly create an object of type `std::ostream`, e.g., using `std::ostream out(new std::filebuf("example.log"));` (note that this code requires some work to avoid a memory leak but it could be done by registering suitable callbacks). – Dietmar Kühl Dec 25 '13 at 16:14
  • You mentioned `ofstream`/`ostringstream` in your question, not `ostream` directly. How would it work for them? –  Dec 25 '13 at 16:16
  • @hvd: Based on your argument only `final` classes should be publicly movable and that is certainly not the case of other type (e.g. `std::string` or `std::vector` although it is probably ill-advised to derive from these classes in the first place). I suspected that the potential of accidentally slicing streams would be the reason although the link Howard posted points at additional issues. – Dietmar Kühl Dec 25 '13 at 16:25
  • Oh, I'm neither saying that it should be publicly movable, nor that it shouldn't be. I'm saying I think that even if it were publicly movable, it wouldn't work for you (based on what you put in the question). –  Dec 25 '13 at 16:27
  • I'm not sure I understand the "clearly". Streams could have been designed as refcounted pointers to stream implementation objects and that would have left them clearly copiable, assignable and swappable with an obvious semantic. – 6502 Dec 25 '13 at 16:33
  • @6502: It seems a reference counted stream is something built on top of a simpler stream interface (some say it is already too complicated). In it simplest form, a reference counted stream could be implemented as `std::shared_ptr` (which would also accommodate derived stream types) or it could use a derived type primarily maintaining a reference count to a stream buffer. – Dietmar Kühl Dec 25 '13 at 16:38

1 Answers1

60

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 4
    Hmm. I see the problem but I’m not sure I agree that the solution is to make them non-movable. Rather, the implementations should be changed so that the code has the expected behaviour (by erasing the actual type of the stream, I’d assume). FWIW Dietmar has presented a compelling use-case for why streams *should* be movable (I’ve had the same problem before). – Konrad Rudolph Dec 25 '13 at 16:26
  • 1
    @KonradRudolph: The problem is that the custom stream buffer is typically embedded into the actual object rather than allocated on the heap. Mandating `std::ostream`s to be movable would mandate that the stream buffer is differently allocated which isn't viable (it could be doable for the standard stream classes but not for existing user-defined streams). I guess, the use case for returning streams of unknown type is better addressed differently. – Dietmar Kühl Dec 25 '13 at 16:34
  • @HowardHinnant the derived `ofstream` is still movable right? – Koushik Shetty Dec 26 '13 at 11:33
  • 1
    @Koushik: Yes, `ofstream` (and other "concrete" stream types) are movable and swappable. – Howard Hinnant Dec 26 '13 at 16:33
  • I am trying to understand the logic. Besides the inconstencies shown in the link. Is correct to also say that ostreams shouldn't be movable because otherwise one could move or swap from std::cout? – alfC Feb 03 '18 at 08:43
  • 1
    That's a nice side effect, but it was never a motivating factor. One can do pretty destructive things to `std::cout` regardless. For example `std::cout.rdbuf(nullptr);` is still perfectly legal (but unadvisable). – Howard Hinnant Feb 03 '18 at 14:11