18 Concepts library [concepts]

18.4 Language-related concepts [concepts.lang]

18.4.9 Concept Swappable [concept.swappable]

Let t1 and t2 be equality-preserving expressions that denote distinct equal objects of type T, and let u1 and u2 similarly denote distinct equal objects of type U.
t1 and u1 can denote distinct objects, or the same object.
end note
An operation exchanges the values denoted by t1 and u1 if and only if the operation modifies neither t2 nor u2 and:
  • If T and U are the same type, the result of the operation is that t1 equals u2 and u1 equals t2.
  • If T and U are different types that model CommonReference<const T&, const U&>, the result of the operation is that C(t1) equals C(u2) and C(u1) equals C(t2) where C is common_­reference_­t<const T&, const U&>.
The name ranges::swap denotes a customization point object ([customization.point.object]).
The expression ranges::swap(E1, E2) for some subexpressions E1 and E2 is expression-equivalent to an expression S determined as follows:
  • S is (void)swap(E1, E2)222 if E1 or E2 has class or enumeration type ([basic.compound]) and that expression is valid, with overload resolution performed in a context that includes the declarations
    template<class T>
      void swap(T&, T&) = delete;
    template<class T, size_t N>
      void swap(T(&)[N], T(&)[N]) = delete;
    and does not include a declaration of ranges::swap.
    If the function selected by overload resolution does not exchange the values denoted by E1 and E2, the program is ill-formed with no diagnostic required.
  • Otherwise, if E1 and E2 are lvalues of array types ([basic.compound]) with equal extent and ranges::swap(*E1, *E2) is a valid expression, S is (void)ranges::swap_­ranges(E1, E2), except that noexcept(S) is equal to noexcept(​ranges::swap(*E1, *E2)).
  • Otherwise, if E1 and E2 are lvalues of the same type T that models MoveConstructible<T> and Assignable<T&, T>, S is an expression that exchanges the denoted values.
    S is a constant expression if
    • T is a literal type ([basic.types]),
    • both E1 = std::move(E2) and E2 = std::move(E1) are constant subexpressions ([defns.const.subexpr]), and
    • the full-expressions of the initializers in the declarations
      T t1(std::move(E1));
      T t2(std::move(E2));
      are constant subexpressions.
    noexcept(S) is equal to is_­nothrow_­move_­constructible_­v<T> && is_­nothrow_­move_­assignable_­v<T>.
  • Otherwise, ranges::swap(E1, E2) is ill-formed.
    This case can result in substitution failure when ranges::swap(E1, E2) appears in the immediate context of a template instantiation.
    end note
Whenever ranges::swap(E1, E2) is a valid expression, it exchanges the values denoted by E1 and E2 and has type void.
end note
template<class T> concept Swappable = requires(T& a, T& b) { ranges::swap(a, b); };
template<class T, class U> concept SwappableWith = CommonReference<const remove_reference_t<T>&, const remove_reference_t<U>&> && requires(T&& t, U&& u) { ranges::swap(std::forward<T>(t), std::forward<T>(t)); ranges::swap(std::forward<U>(u), std::forward<U>(u)); ranges::swap(std::forward<T>(t), std::forward<U>(u)); ranges::swap(std::forward<U>(u), std::forward<T>(t)); };
The semantics of the Swappable and SwappableWith concepts are fully defined by the ranges::swap customization point.
end note
User code can ensure that the evaluation of swap calls is performed in an appropriate context under the various conditions as follows:
#include <cassert>
#include <concepts>
#include <utility>

namespace ranges = std::ranges;

template<class T, std::SwappableWith<T> U>
void value_swap(T&& t, U&& u) {
  ranges::swap(std::forward<T>(t), std::forward<U>(u));

template<std::Swappable T>
void lv_swap(T& t1, T& t2) {
  ranges::swap(t1, t2);

namespace N {
  struct A { int m; };
  struct Proxy { A* a; };
  Proxy proxy(A& a) { return Proxy{ &a }; }

  void swap(A& x, Proxy p) {
    ranges::swap(x.m, p.a->m);
  void swap(Proxy p, A& x) { swap(x, p); }      // satisfy symmetry requirement

int main() {
  int i = 1, j = 2;
  lv_swap(i, j);
  assert(i == 2 && j == 1);

  N::A a1 = { 5 }, a2 = { -5 };
  value_swap(a1, proxy(a2));
  assert(a1.m == -5 && a2.m == 5);
end example
The name swap is used here unqualified.