Just when I thought I understand the multi-pass move construction of iostreams in C++11 (thanks to https://stackoverflow.com/a/8156356/273767 for the intro), I ran into this:
§27.7.2.5.1[iostream.cons]/3
basic_iostream(basic_iostream&& rhs);
3
Effects: Move constructs from the rvalue rhs by constructing thebasic_istream
base class withmove(rhs)
.
So what happens to the other base, basic_ostream
?
I see libc++ gave std::basic_ostream
a protected default constructor , which is called here (and also, in contradiction to the letter of §27.7.2.5.1/1, in the normal constructor of basic_iostream ), and does nothing. Is that how it's supposed to be?
As you point out, the spec for:
explicit basic_iostream(basic_streambuf<charT,traits>* sb);
initializes both bases. I've never been ok with that:
http://cplusplus.github.com/LWG/lwg-closed.html#135
as it causes the single basic_ios::init()
function to be called twice on the same virtual base object. The committee ruled that this double initialization was harmless. I disagreed strongly enough that I refused to implement the specification in regards to this detail. But the spec says to double initialize the virtual base class.
When it came time to specify the basic_iostream
move constructor, I was in the driver's seat. And so I specified it how I thought best (to not doubly initialize basic_ios
). That decision has yet to be challenged, but probably will be eventually.
Note that in order to avoid the double initialization, the basic_ostream
default constructor has to be carefully crafted to do absolutely nothing. And by nothing I really mean nothing. No zero initialization:
protected:
_LIBCPP_ALWAYS_INLINE
basic_ostream() {} // extension, intentially does not initialize
Fortunately the base classes of basic_ostream
are actually specified to do nothing in their default constructors. So everything just works: basic_ostream
default constructs and doesn't touch memory. Then derived clients call init(basic_streambuf<char_type, traits_type>*)
exactly once to do the actual construction of basic_ios
/ ios_base
.
It's a really messy design. By refusing to double initialize the virtual base, I feel libc++ makes the design a little less messy, and a little more reliable. This is standard behavior for the move constructor, and not standard behavior for the constructor taking a streambuf*
.
I agree with Howard that this is kind of a wart on the standard spec.
In my implementation, I chose to use a more specific call to a constructor extension
basic_iostream(basic_iostream&& _Other)
: std::basic_istream<char_type, traits_type>(std::move(_Other)),
std::basic_ostream<char_type, traits_type>(basic_ostream::_NoInit)
{ }
using a special protected
constructor in the base class
protected:
// special interface for basic_iostream
enum __no_init_t { _NoInit };
basic_ostream(__no_init_t)
{ }
The net effect is the same.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.