Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `basic_ios::swap` only do a partial swap?

Tags:

c++

c++11

swap

C++11 §27.5.4.2/21:

void swap(basic_ios& rhs);

Effects: The states of *this and rhs shall be exchanged, except that rdbuf() shall return the same value as it returned before the function call, and rhs.rdbuf() shall return the same value as it returned before the function call.

What is this partial swapping useful for?

Can it cause trouble?

like image 823
Cheers and hth. - Alf Avatar asked Nov 16 '11 06:11

Cheers and hth. - Alf


2 Answers

You can blame me for this one. The committee has tried to change (twice I think), but each time the solution ended up breaking things.

Swap and move semantics was retrofitted onto our I/O system a decade after it was designed. And it wasn't a perfectly clean fit.

Note that basic_ios::swap is a protected member function and there is no namespace-scope variant. Thus this can only be called from a derived class (typically istream/ostream). Note that i/o_stream::swap is also protected and with no namespace-scope variant. And their spec is to call the base class swap and then swap any local data (such as the gcount in istream).

Finally up at the string/filestream level you get what you would consider a "normal" swap: public member and namespace-scope variants. At this level you've got a data member string/file buffer (the rdbuf) and the base class. The swap at this level simply swaps the base and data members.

The complicating characteristic of all this is that the rdbuf() down in the base class is actually a self-referencing pointer to the derived class's streambuf (basic_filebuf or basic_stringbuf) and that is why you don't want the base class to swap these self-referencing pointers.

This makes the base swap weird, but everyone is protected from it except the derived clients. And the code for the derived client's swap is subsequently deceptively simple looking. And at the derived level, swap is made public and behaves in the manner that public clients expect it to.

A similar dance is made for move construction and move assignment. Move construction is further complicated by the fact that the base class is a virtual base, and thus its constructor is not called by the most directly derived class.

It was fun. It looks weird. But it ultimately works. ;-)

Slight Correction:

Alberto Ganesh Barbati is responsible for protecting swap at the i/ostream level. It was a very good call on his part that I had completely missed with my first design.

like image 73
Howard Hinnant Avatar answered Oct 06 '22 10:10

Howard Hinnant


I only have one speculative answer...

If the author assumed that a stream may use an internal buffer (for example a char buffer[50] data member), then this provision is necessary as obviously the content of the buffers may be swapped, but their address will remain unchanged.

I do not know whether it is actually allowed or not.

like image 34
Matthieu M. Avatar answered Oct 06 '22 10:10

Matthieu M.