Not exception-safe default move constructors (demonstrated on std::pair)

Daniel Krügler daniel.kruegler@gmail.com
Thu Feb 21 22:20:00 GMT 2013


2013/2/21 Dmitry Marakasov <amdmi3@amdmi3.ru>:
> Hi!
>
> I'm not really sure which (g++ or libstdc++) problem this really is, so
> posting into both lists.

It doesn't look like a problem of libstdc++ to me (see below why).

> The problem is described here:
> http://cpp-next.com/archive/2009/10/exceptionally-moving/
>
> - You have two classes:
>   A, which has a proper move constructor which doesn't throw
>   B, which which only has copy constructor which may throw
> - You have an std::pair<A, B>
> - You move a pair
>   std::pair<A, B> pair1;
>   try {
>     std::pair<A, B> pair2(std::move(a));
>   }
>
> What happens:
> - A part is moved with move constructor. This is obviously destructive
>   to pair1.
> - B part is copied as it doesn't have move constuctor.
> - Now, that copy constructor throws (which is pretty expectable is it
> likely allocates resources). After this, as A part was already moved,
> we're left with corrupted pair1 (with empty A part).

I agree that this is the current to be expected behaviour. But it is
what the standard currently requires:

template <class T1, class T2>
struct pair {
  [..]
  pair(const pair&) = default;
  pair(pair&&) = default;
  [..]
};

> The same problem applies to most containers, but, for example, in
> std::vector it is solved gracefully:
>
> --- bits/vector.tcc from gcc-4.8:
>       pointer __tmp = _M_allocate_and_copy(__n,
>               _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start),
>               _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish));
> ---
>
> so move semantics are only used IF the type has non-throwing(noexcept)
> move constructor.
>
> However, with pair this does not apply:
>
> --- bits/stl_pair.h from gcc-4.8:
> struct pair {
>     ...
>     constexpr pair(pair&&) = default;
> ---

I agree with the divergence relative to containers, but the current
specification imposes this requirement. User-code could observe any
difference (you do, for example), therefore it is hard to change.

I suggest that you instead send an LWG issue submission request as
described here:

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#submit_issue

> Either the default constructor generated by gcc is unsafe, or there
> should be custom, safe move constuctor with corresponding enable_if.
>
> I lean towards the former, as safe default move constuctors should be
> always generated probably, and specific ones may be really hard to
> implement for more complex types (tuples).

While I agree with your arguments this is primarily a standard Library
specification problem.

> I've written a small program which demonstrates the problem:
>
> https://gist.github.com/AMDmi3/5005557
>
> tested it on FreeBSD with gcc-4.8 and on Debian with gcc-4.7, and it
> showed problem on both.
>
> Is this really a gcc problem? Should I file a bug?

I don't think so. I suggest to send an LWG issue request instead.

- Daniel



More information about the Libstdc++ mailing list