This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.

### 2153. Narrowing of the non-member `swap` contract

Section: 22.2.2 [utility.swap], 16.4.4.3 [swappable.requirements], 24.2.2.1 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2020-10-02

Priority: 2

View all other issues in [utility.swap].

View all issues with LEWG status.

Discussion:

Sub-clause 22.2.2 [utility.swap] defines a non-member 'swap' function with defined behavior for all `MoveConstructible` and `MoveAssignable` types. It does not guarantee constant-time complexity or `noexcept` in general, however this definition does render all objects of `MoveConstructible` and `MoveAssignable` type swappable (by the unary definition of sub-clause 16.4.4.3 [swappable.requirements]) in the absence of specializations or overloads.

The overload of the non-member `swap` function defined in Table 96, however, defines semantics incompatible with the generic non-member `swap` function, since it is defined to call a member `swap` function whose semantics are undefined for some values of `MoveConstructible` and `MoveAssignable` types.

The obvious (perhaps naive) interpretation of sub-clause 16.4.4.3 [swappable.requirements] is as a guide to the "right" semantics to provide for a non-member `swap` function (called in the context defined by 16.4.4.3 [swappable.requirements] p3) in order to provide interoperable user-defined types for generic programming. The standard container types don't follow these guidelines.

More generally, the design in the standard represents a classic example of "contract narrowing". It is entirely reasonable for the contract of a particular `swap` overload to provide more guarantees, such as constant-time execution and `noexcept`, than are provided by the `swap` that is provided for any `MoveConstructible` and `MoveAssignable` types, but it is not reasonable for such an overload to fail to live up to the guarantees it provides for general types when it is applied to more specific types. Such an overload or specialization in generic programming is akin to an override of an inherited virtual function in OO programming: violating a superclass contract in a subclass may be legal from the point of view of the language, but it is poor design and can easily lead to errors. While we cannot prevent user code from providing overloads that violate the more general `swap` contract, we can avoid doing so within the library itself.

My proposed resolution is to draw a sharp distinction between member `swap` functions, which provide optimal performance but idiosyncratic contracts, and non-member `swap` functions, which should always fulfill at least the contract of 22.2.2 [utility.swap] and thus render objects swappable. The member `swap` for containers with non-propagating allocators, for example, would offer constant-time guarantees and `noexcept` but would only offer defined behavior for values with allocators that compare equal; non-member `swap` would test allocator equality and then dispatch to either member `swap` or `std::swap` depending on the result, providing defined behavior for all values (and rendering the type "swappable"), but offering neither the constant-time nor the `noexcept` guarantees.

[2013-03-15 Issues Teleconference]

Moved to Open.

This topic deserves more attention than can be given in the telecon, and there is no proposed resolution.

[2016-03 Jacksonville]

Alisdair says that his paper P0178 addresses this.

[2016-08 Chicago]

Send to LEWG

[2016-06 Oulu]

P0178 reviewed, and sent back to LEWG for confirmation.

Thursday Morning: A joint LWG/LEWG meeting declined to adopt P0178.

[2020-10-02; remove P0178 as Proposed Resolution]

Proposed resolution: