Files
2025-10-25 03:02:53 +03:00

156 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[concept.swappable]
# 18 Concepts library [[concepts]](./#concepts)
## 18.4 Language-related concepts [[concepts.lang]](concepts.lang#concept.swappable)
### 18.4.9 Concept swappable [concept.swappable]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/concepts.tex#L566)
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[.](#1.sentence-1)
[*Note [1](#note-1)*:
t1 and u1 can denote distinct objects, or the same object[.](#1.sentence-2)
— *end note*]
An operation[*exchanges the values*](#def:exchanges_the_values) denoted by t1 and u1 if and only
if the operation modifies neither t2 nor u2 and:
- [(1.1)](#1.1)
If T and U are the same type, the result of the operation
is that t1 equals u2 and u1 equals t2[.](#1.1.sentence-1)
- [(1.2)](#1.2)
If T and U are different types and [common_reference_with](concept.commonref#concept:common_reference_with "18.4.5Concept common_­reference_­with[concept.commonref]")<decltype((t1)), decltype((u1))> is modeled,
the result of the operation is that C(t1) equals C(u2) and C(u1) equals C(t2) where C is common_reference_t<decltype((t1)), decltype((u1))>[.](#1.2.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/concepts.tex#L590)
The name ranges::swap denotes a customization point
object ([[customization.point.object]](customization.point.object "16.3.3.3.5Customization Point Object types"))[.](#2.sentence-1)
The expressionranges::swap(E1, E2) for subexpressions E1 and E2 is expression-equivalent to an expressionS determined as follows:
- [(2.1)](#2.1)
S is (void)swap(E1, E2)[192](#footnote-192 "The name swap is used here unqualified.") if E1 or E2 has class or enumeration type ([[basic.compound]](basic.compound "6.9.4Compound types")) and that expression is valid, with
overload resolution performed in a context that includes the declarationtemplate<class T>void swap(T&, T&) = delete; and does not include a declaration of ranges::swap[.](#2.1.sentence-1)
If the function selected by overload resolution does not
exchange the values denoted by E1 and E2,
the program is ill-formed, no diagnostic required[.](#2.1.sentence-2)
[*Note [2](#note-2)*:
This precludes calling unconstrained program-defined overloads of swap[.](#2.1.sentence-3)
When the deleted overload is viable, program-defined overloads
need to be more specialized ([[temp.func.order]](temp.func.order "13.7.7.3Partial ordering of function templates")) to be selected[.](#2.1.sentence-4)
— *end note*]
- [(2.2)](#2.2)
Otherwise, if E1 and E2 are lvalues of array types ([[basic.compound]](basic.compound "6.9.4Compound types"))
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))[.](#2.2.sentence-1)
- [(2.3)](#2.3)
Otherwise, if E1 and E2 are lvalues of the
same type T that models [move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<T> and [assignable_from](concept.assignable#concept:assignable_from "18.4.8Concept assignable_­from[concept.assignable]")<T&, T>, S is an expression that exchanges the denoted values[.](#2.3.sentence-1)
S is a constant expression if
* [(2.3.1)](#2.3.1)
T is a literal type ([[basic.types.general]](basic.types.general#term.literal.type "6.9.1General")),
* [(2.3.2)](#2.3.2)
both E1 = std::move(E2) and E2 = std::move(E1) are
constant subexpressions ([[defns.const.subexpr]](defns.const.subexpr "3.15constant subexpression")), and
* [(2.3.3)](#2.3.3)
the full-expressions of the initializers in the declarationsT 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>[.](#2.3.sentence-2)
- [(2.4)](#2.4)
Otherwise, ranges::swap(E1, E2) is ill-formed[.](#2.4.sentence-1)
[*Note [3](#note-3)*:
This case can result in substitution failure when ranges::swap(E1, E2) appears in the immediate context of a template instantiation[.](#2.4.sentence-2)
— *end note*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/concepts.tex#L660)
[*Note [4](#note-4)*:
Whenever ranges::swap(E1, E2) is a valid expression, it
exchanges the values denoted byE1 and E2 and has type void[.](#3.sentence-1)
— *end note*]
[🔗](#concept:swappable)
`template<class T>
concept [swappable](#concept:swappable "18.4.9Concept swappable[concept.swappable]") = requires(T& a, T& b) { ranges::swap(a, b); };
`
[🔗](#concept:swappable_with)
`template<class T, class U>
concept [swappable_with](#concept:swappable_with "18.4.9Concept swappable[concept.swappable]") =
[common_reference_with](concept.commonref#concept:common_reference_with "18.4.5Concept common_­reference_­with[concept.commonref]")<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));
};
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/concepts.tex#L684)
[*Note [5](#note-5)*:
The semantics of the [swappable](#concept:swappable "18.4.9Concept swappable[concept.swappable]") and [swappable_with](#concept:swappable_with "18.4.9Concept swappable[concept.swappable]") concepts are fully defined by the ranges::swap customization point object[.](#4.sentence-1)
— *end note*]
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/concepts.tex#L690)
[*Example [1](#example-1)*:
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::[swappable_with](#concept:swappable_with "18.4.9Concept swappable[concept.swappable]")<T> U>void value_swap(T&& t, U&& u) { ranges::swap(std::forward<T>(t), std::forward<U>(u));}template<std::[swappable](#concept:swappable "18.4.9Concept swappable[concept.swappable]") T>void lv_swap(T& t1, T& t2) { ranges::swap(t1, t2);}namespace N {struct A { int m; }; struct Proxy { A* a;
Proxy(A& a) : a{&a} {}friend void swap(Proxy x, Proxy y) { ranges::swap(*x.a, *y.a); }};
Proxy proxy(A& a) { return Proxy{ a }; }}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*]
[192)](#footnote-192)[192)](#footnoteref-192)
The name swap is used
here unqualified[.](#footnote-192.sentence-1)