Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are the swap member functions in STL containers not declared noexcept?

As of N3797 the C++ standard requires swap functions of containers to not throw any exceptions unless specified otherwise [container.requirements.general] (23.2.1§10).

  • Why are the swap member functions that are specified to not throw not declared noexcept?

The same question applies to the specialized non-member swap overloads.

like image 987
Ralph Tandetzky Avatar asked May 20 '14 08:05

Ralph Tandetzky


2 Answers

Further to what refp said, here's a post from Daniel Krügler on the std-discussion mailing list:

The internal policy to declare a function as unconditional noexcept is explained in

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3279.pdf

With the terminology used in that paper, std::vector's swap function has a narrowing contract, that is it has preconditions in regard to the allocators of the participating objects. This means, there exists the possibility that callers may violate the preconditions and an implementation should be allowed to signal this my different means than by termination. Therefore such functions should not be noexcept, but it should have an effective element "Throws: Nothing" because this applies to the situation when the preconditions are satisfied.

(link)

Said internal policy is the canonical, official answer to your question.

like image 134
Lightness Races in Orbit Avatar answered Sep 28 '22 20:09

Lightness Races in Orbit


It may sound odd at first, but not explicitly stating that swap of standard container is noexcept is intentional; and it all boils down to undefined behavior (UB).


23.2.1p9 General Container Requirements [container.requirements.general]

The expression a.swap(b), for containers a and b of a standard container type other than array, shall exchange the values of a and bwithout invoking any move, copy, or swap operations on the individual container elements.

Any Compare, Pred, or Hash objects beloning to a and b shall be swappable and shall be exchanged by unqualified calls to non-member swap.

If allocator_traits<allocator_type>::propagate_on_container_swap::value is true, then the allocators of a and b shall also be exchanged using an unqalified call to non-member swap. Otherwise, they shall not be swapped, and the behavior is undefined unless `a.get_allocator () == b.get_allocator ().

Note: italics added by me.


Why is the previous section relevant to my question?

Since the swap of standard containers has a precondition (most importantly the last paragraph of the previously quoted section of the Standard), that may lead to UB if not satisfied, the Standard doesn't want to impose "impossible" constraints on implementations.


The Standard says the following about undefined behaviour:

1.3.24 undefined behavior [defns.undefined]

Behavior for which this International Standard imposes no requirements.


Only criminals, and perhaps salesmen, would consider a No to be something other than a No, but when the Standard says "no requirements" it really means "no requirements"; marking the relevant swap functions as noexcept would impose a requirement on implementations, where there should be none.


Why doesn't the Standard want to impose such requirements?

There's an interesting paper (N3248) on this matter by Alisdair Meredith and John Lakos titled "noexcept prevents Library Validation".

In short it talks about how noexcept will prevent library implementation to use asserts in library code (ie. implementations of the standard library), even during debug mode, and the implications of that.

If C++ had a standardized "testing" vs "production" mode (as the paper calls it), where noexcept would conditionally apply, this would be far less problematic.. but as it currently stands; C++ has no "modes".

like image 36
Filip Roséen - refp Avatar answered Sep 28 '22 18:09

Filip Roséen - refp