Title
Narrowing of the non-member swap contract
Status
lewg
Section
[utility.swap] [swappable.requirements] [container.requirements.general]
Submitter
Robert Shearer

Created on 2012-04-13.00:00:00 last changed 93 months ago

Messages

Date: 2016-08-02.18:48:27
Apply

Proposed resolution:

P0178.
Date: 2018-06-23.00:00:44

[ 2016-06 Oulu ]

P0178 reviewed, and sent back to LEWG for confirmation.

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

Date: 2020-10-02.21:04:21

[ 2016-06 Oulu ]

P0178 reviewed, and sent back to LEWG for confirmation.

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

Date: 2020-10-02.21:04:21

[ 2016-08 Chicago ]

Send to LEWG

Date: 2020-10-02.21:04:21

[ 2016-03 Jacksonville ]

Alisdair says that his paper P0178 addresses this.

Date: 2013-03-15.00:00:00

[ 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.

Date: 2012-08-11.20:19:27

Sub-clause [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 [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 [swappable.requirements] is as a guide to the "right" semantics to provide for a non-member swap function (called in the context defined by [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 [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.

History
Date User Action Args
2016-08-02 18:48:27adminsetmessages: + msg8339
2016-08-02 18:48:27adminsetmessages: + msg8338
2016-08-02 17:19:11adminsetmessages: + msg8323
2016-08-02 17:19:11adminsetstatus: open -> lewg
2016-03-08 22:51:25adminsetmessages: + msg8021
2016-03-08 22:51:25adminsetmessages: + msg8020
2013-03-18 14:33:00adminsetmessages: + msg6408
2013-03-18 13:02:36adminsetstatus: new -> open
2012-08-05 11:46:21adminsetmessages: + msg6092
2012-04-13 00:00:00admincreate