Title
Swapping non-swappable types
Status
new
Section
[pairs.spec][tuple.special][optional.specalg] [variant.specalg][unique.ptr.special][array.special] [queue.special][priqueue.special][stack.special]
Submitter
Agustín K-ballo Bergé

Created on 2016-08-15.00:00:00 last changed 60 months ago

Messages

Date: 2019-04-17.11:42:45

Proposed resolution:

This wording is relative to N4810.

  1. Modify [pairs.spec] as indicated:

    template<class T1, class T2>
      constexpr void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));
    

    -7- Effects: As if by x.swap(y).

    -8- Remarks: This function shall not participate in overload resolutionis defined as deleted unless is_swappable_v<T1> is true and is_swappable_v<T2> is true.

  2. Modify [tuple.special] as indicated:

    template <class... Types>
      constexpr void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);
    

    -?- Effects: As if by x.swap(y).

    -1- Remarks: This function shall not participate in overload resolutionis defined as deleted unless is_swappable_v<Ti> is true for all i, where 0 <= i and i < sizeof...(Types). The expression inside noexcept is equivalent to:

    noexcept(x.swap(y))
    

    -2- Effects: As if by x.swap(y).

  3. Modify [optional.specalg] as indicated:

    template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Effects: Calls x.swap(y).

    -2- Remarks: This function shall not participate in overload resolutionis defined as deleted unless is_move_constructible_v<T> is true and is_swappable_v<T> is true.

  4. Modify [variant.specalg] as indicated:

    template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);
    

    -1- Effects: Equivalent to v.swap(w).

    -2- Remarks: This function shall not participate in overload resolutionis defined as deleted unless is_move_constructible_v<Ti> && is_swappable_v<Ti> is true for all i. The expression inside noexcept is equivalent to noexcept(v.swap(w)).

  5. Modify [unique.ptr.special] as indicated:

    template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
    

    -?- Effects: Calls x.swap(y).

    -1- Remarks: This function shall not participate in overload resolutionis defined as deleted unless is_swappable_v<D> is true.

    -2- Effects: Calls x.swap(y).

  6. Modify [array.special] as indicated:

    template <class T, size_t N>
      void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Constraints: N == 0 or is_swappable_v<T> is true.

    -2- Effects: As if by x.swap(y).

    -3- Complexity: Linear in N.

    -?- Remarks: This function is defined as deleted unless N == 0 or is_swappable_v<T> is true.

Date: 2019-04-17.00:00:00

[ 2019-04-17 Jonathan updates proposed resolution based on Ville's 2016-11-17 observation that the container adaptors always require swappable sequences anyway. The new proposed resolution is based on the latest WP, "de-shalled", and Remarks elements are repositioned after the Effects. ]

Date: 2019-04-17.11:42:45

Related: 2748 swappable traits for optionals, 2749 swappable traits for variants.

The adoption of P0185R1 "Adding [nothrow-]swappable traits" makes certain non-swappable types indirectly swappable. Consider a type defined as follows:

struct non_swappable {
  friend void swap(non_swappable&, non_swappable&) = delete;
};

non_swappable ns1, ns2;
using std::swap;
swap(ns1, ns2); // ill-formed

static_assert(std::is_swappable_v<non_swappable> == false); // holds

Lvalues of type non_swappable are not swappable, as defined by [swappable.requirements], overload resolution selects the deleted function. Consistently, is_swappable_v<non_swappable> yields false. It should be noted that since non_swappable is move constructible and move assignable, a qualified call to std::swap would be well-formed, even under P0185. Now consider the following snippet:

std::tuple<non_swappable> tns1, tns2;
using std::swap;
swap(tns1, tns2); // previously ill-formed, now well-formed

static_assert(std::is_swappable_v<std::tuple<non_swappable>> == false); // fires

Before P0185, this snippet would violate the implicit requirement of specialized swap for tuples that each tuple element be swappable. After P0185, this specialized swap overload for tuples would be SFINAEd away, resulting in overload resolution selecting the base swap overload, and performing the exchange via move construction and move assignment of tuples.

This issue affects all of pair, tuple, unique_ptr, array, queue, priority_queue, stack, and should eventually also apply to optional and variant.

Previous resolution [SUPERSEDED]:

This wording is relative to N4606, except when otherwise noted.

  1. Modify [pairs.spec] as indicated:

    template<class T1, class T2> void swap(pair<T1, T2>& x, pair<T1, T2>& y)
      noexcept(noexcept(x.swap(y)));
    

    -7- Effects: As if by x.swap(y).

    -8- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<T1> is true and is_swappable_v<T2> is true.

  2. Modify [tuple.special] as indicated:

    template <class... Types>
      void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<Ti> is true for all i, where 0 <= i and i < sizeof...(Types). The expression inside noexcept is equivalent to:

    noexcept(x.swap(y))
    

    -2- Effects: As if by x.swap(y).

  3. Modify [unique.ptr.special] as indicated:

    template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<D> is true.

    -2- Effects: Calls x.swap(y).

  4. Modify [array.special] as indicated:

    template <class T, size_t N>
      void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless N == 0 or is_swappable_v<T> is true.

    -2- Effects: As if by x.swap(y).

    […]

  5. Modify [queue.special] as indicated:

    template <class T, class Container>
      void swap(queue<T, Container>& x, queue<T, Container>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<Container> is true.

    -2- Effects: As if by x.swap(y).

  6. Modify [priqueue.special] as indicated:

    template <class T, class Container, class Compare>
      void swap(priority_queue<T, Container, Compare>& x,
                priority_queue<T, Container, Compare>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<Container> is true and is_swappable_v<Compare> is true.

    -2- Effects: As if by x.swap(y).

  7. Modify [stack.special] as indicated:

    template <class T, class Container>
      void swap(stack<T, Container>& x, stack<T, Container>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<Container> is true.

    -2- Effects: As if by x.swap(y).

  8. Modify [optional.specalg] as indicated:

    This change should be performed if and only if LWG 2748 is accepted and is against the wording of 2748:

    template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));
    

    -1- Effects: Calls x.swap(y).

    -2- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_move_constructible_v<T> is true and is_swappable_v<T> is true.

  9. Modify [variant.specalg] as indicated:

    This change should be performed if and only if LWG 2749 is accepted and is against the wording of 2749:

    template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);
    

    -1- Effects: Equivalent to v.swap(w).

    -2- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless is_move_constructible_v<Ti> && is_swappable_v<Ti> is true for all i. The expression inside noexcept is equivalent to noexcept(v.swap(w)).

History
Date User Action Args
2019-04-17 11:42:45adminsetmessages: + msg10394
2016-09-04 17:09:05adminsetmessages: + msg8488
2016-08-15 00:00:00admincreate