This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.

### 911. I/O streams and `move/swap` semantic

Section: 31.7.5 [input.streams], 31.7.6 [output.streams] Status: C++11 Submitter: Alberto Ganesh Barbati Opened: 2008-09-29 Last modified: 2016-01-28

Priority: Not Prioritized

View all issues with C++11 status.

Discussion:

Class template `basic_istream`, `basic_ostream` and `basic_iostream` implements public move constructors, move assignment operators and `swap` method and free functions. This might induce both the user and the compiler to think that those types are `MoveConstructible`, `MoveAssignable` and `Swappable`. However, those class templates fail to fulfill the user expectations. For example:

>     std::ostream os(std::ofstream("file.txt"));
>     assert(os.rdbuf() == 0); // buffer object is not moved to os, file.txt has been closed

>     std::vector<std::ostream> v;
>     v.push_back(std::ofstream("file.txt"));
>     v.reserve(100); // causes reallocation
>     assert(v[0].rdbuf() == 0); // file.txt has been closed!

>     std::ostream&& os1 = std::ofstream("file1.txt");
>     os1 = std::ofstream("file2.txt");
>     os1 << "hello, world"; // still writes to file1.txt, not to file2.txt!

>     std::ostream&& os1 = std::ofstream("file1.txt");
>     std::ostream&& os2 = std::ofstream("file2.txt");
>     std::swap(os1, os2);
>     os1 << "hello, world"; // writes to file1.txt, not to file2.txt!

This is because the move constructor, the move assignment operator and `swap` are all implemented through calls to `std::basic_ios` member functions `move()` and `swap()` that do not move nor swap the controlled stream buffers. That can't happen because the stream buffers may have different types.

Notice that for `basic_streambuf`, the member function `swap()` is protected. I believe that is correct and all of `basic_istream`, `basic_ostream`, `basic_iostream` should do the same as the move ctor, move assignment operator and swap member function are needed by the derived `fstream`s and `stringstream`s template. The free swap functions for `basic_(i|o|io)stream` templates should be removed for the same reason.

[ Batavia (2009-05): ]

> We note that the rvalue swap functions have already been removed.

> Bill is unsure about making the affected functions protected; he believes they may need to be public.

> We are also unsure about removing the lvalue swap functions as proposed.

> Move to Open.

[ 2009-07 Frankfurt: ]

> It's not clear that the use case is compelling.

> Howard: This needs to be implemented and tested.

[ 2009-07-26 Howard adds: ]

> I started out thinking I would recommend NAD for this one. I've turned around to agree with the proposed resolution (which I've updated to the current draft). I did not fully understand Ganesh's rationale, and attempt to describe my improved understanding below.

> The move constructor, move assignment operator, and swap function are different for `basic_istream`, `basic_ostream` and `basic_iostream` than other classes. A timely conversation with Daniel reminded me of this long forgotten fact. These members are sufficiently different that they would be extremely confusing to use in general, but they are very much needed for derived clients.

> - The move constructor moves everything but the `rdbuf` pointer.
> - The move assignment operator moves everything but the `rdbuf` pointer.
> - The swap function swaps everything but the `rdbuf` pointer.

> The reason for this behavior is that for the std-derived classes (stringstreams, filestreams), the `rdbuf` pointer points back into the class itself (self referencing). It can't be swapped or moved. But this fact isn't born out at the `stream` level. Rather it is born out at the `fstream`/`sstream` level. And the lower levels just need to deal with that fact by not messing around with the `rdbuf` pointer which is stored down at the lower levels.

> In a nutshell, it is very confusing for all of those who are not so intimately related with streams that they've implemented them. And it is even fairly confusing for some of those who have (including myself). I do not think it is safe to swap or move `istreams` or `ostreams` because this will (by necessary design) separate stream state from streambuffer state. Derived classes (such as `fstream` and `stringstream` must be used to keep the stream state and stream buffer consistently packaged as one unit during a move or swap.

> I've implemented this proposal and am living with it day to day.

[ 2009 Santa Cruz: ]

> Leave Open. Pablo expected to propose alternative wording which would rename move construction, move assignment and swap, and may or may not make them protected. This will impact issue 900.

[ 2010 Pittsburgh: ]

> Moved to Ready for Pittsburgh.

Proposed resolution:

31.7.5.2 [istream]: make the following member functions protected:

>     basic_istream(basic_istream&&  rhs);
>     basic_istream&  operator=(basic_istream&&  rhs);
>     void  swap(basic_istream&  rhs);

Ditto: remove the swap free function signature

>     // swap:
>     template <class charT, class traits>
>       void swap(basic_istream<charT, traits>& x, basic_istream<charT, traits>& y);

31.7.5.2.3 [istream.assign]: remove paragraph 4

>     template <class charT, class traits>
>       void swap(basic_istream<charT, traits>& x, basic_istream<charT, traits>& y);

> > Effects: `x.swap(y)`.

31.7.5.7 [iostreamclass]: make the following member function protected:

>     basic_iostream(basic_iostream&&  rhs);
>     basic_iostream&  operator=(basic_iostream&&  rhs);
>     void  swap(basic_iostream&  rhs);

Ditto: remove the swap free function signature

>     template <class charT, class traits>
>       void swap(basic_iostream<charT, traits>& x, basic_iostream<charT, traits>& y);

31.7.5.7.4 [iostream.assign]: remove paragraph 3

>     template <class charT, class traits>
>       void swap(basic_iostream<charT, traits>& x, basic_iostream<charT, traits>& y);

> > Effects: `x.swap(y)`.

31.7.6.2 [ostream]: make the following member function protected:

>     basic_ostream(basic_ostream&&  rhs);
>     basic_ostream&  operator=(basic_ostream&&  rhs);
>     void  swap(basic_ostream&  rhs);

Ditto: remove the swap free function signature

>     // swap:
>     template <class charT, class traits>
>       void swap(basic_ostream<charT, traits>& x, basic_ostream<charT, traits>& y);

31.7.6.2.3 [ostream.assign]: remove paragraph 4

>     template <class charT, class traits>
>       void swap(basic_ostream<charT, traits>& x, basic_ostream<charT, traits>& y);

> > Effects: `x.swap(y)`.