move/swap
semanticSection: 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
andbasic_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 thestream
level. Rather it is born out at thefstream
/sstream
level. And the lower levels just need to deal with that fact by not messing around with therdbuf
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
orostreams
because this will (by necessary design) separate stream state from streambuffer state. Derived classes (such asfstream
andstringstream
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)
.