|
| 1 | +--- |
| 2 | +title: "C++ named requirements: Swappable" |
| 3 | +cppdoc: |
| 4 | + revision: |
| 5 | + since: C++11 |
| 6 | +--- |
| 7 | + |
| 8 | +import { CppHeader } from "@components/header"; |
| 9 | +import { DR, DRList } from "@components/defect-report"; |
| 10 | +import { Desc, DescList } from "@components/desc-list"; |
| 11 | +import Missing from "@components/Missing.astro"; |
| 12 | +import { Revision, RevisionBlock } from "@components/revision"; |
| 13 | + |
| 14 | +Any lvalue or rvalue of this type can be swapped with any lvalue or rvalue of some other type, using unqualified function call <Missing> `swap()` </Missing> in the context where both `std::swap` and the user-defined `swap()`s are visible. |
| 15 | + |
| 16 | + |
| 17 | +## Requirements |
| 18 | + |
| 19 | +Type U is swappable with type T if, for any object u of type U and any object t of type T, |
| 20 | + |
| 21 | +Given |
| 22 | + |
| 23 | +- `u`, an expression of type `T`. |
| 24 | + |
| 25 | +- `u`, an lvalue expression of type `Key`. |
| 26 | +<table> |
| 27 | + <thead> |
| 28 | + <tr> |
| 29 | +<th> Expression </th> |
| 30 | +<th> Requirements </th> |
| 31 | +<th> Semantics </th> |
| 32 | + </tr> |
| 33 | + </thead> |
| 34 | + <tbody> |
| 35 | + <tr> |
| 36 | + <td> |
| 37 | +```c++ |
| 38 | +#include <algorithm> // until C++11 |
| 39 | +#include <utility> // since C++11 |
| 40 | +using std::swap; |
| 41 | +swap(u, t); |
| 42 | +``` |
| 43 | + </td> |
| 44 | + <td> |
| 45 | +After the call, the value of `t` is the value held by `u` before the call, and the value of `u` is the value held by `t` before the call. |
| 46 | + </td> |
| 47 | + <td> |
| 48 | +Calls the function named `swap()` found by overload resolution among all functions with that name that are found by argument-dependent lookup and the two `std::swap` templates defined in the header |
| 49 | +
|
| 50 | +<RevisionBlock until="C++11" noborder> <CppHeader name="algorithm" /> </RevisionBlock> |
| 51 | +<RevisionBlock since="C++11" noborder> <CppHeader name="utility" /> </RevisionBlock> |
| 52 | + </td> |
| 53 | + </tr> |
| 54 | + <tr> |
| 55 | + <td> |
| 56 | +```c++ |
| 57 | +#include <algorithm> // until C++11 |
| 58 | +#include <utility> // since C++11 |
| 59 | +using std::swap; |
| 60 | +swap(u, t); |
| 61 | +``` |
| 62 | + </td> |
| 63 | + <td> |
| 64 | +Same |
| 65 | + </td> |
| 66 | + <td> |
| 67 | +Same |
| 68 | + </td> |
| 69 | + </tr> |
| 70 | + </tbody> |
| 71 | +</table> |
| 72 | + |
| 73 | +Many standard library functions (for example, many algorithms) expect their arguments to satisfy Swappable, which means that any time the standard library performs a swap, it uses the equivalent of using std::swap; swap(t, u);. |
| 74 | + |
| 75 | +Typical implementations either |
| 76 | + |
| 77 | +1. Define a non-member swap in the enclosing namespace, which may forward to a member swap if access to non-public data members is required. |
| 78 | +2. Define a friend function in-class (this approach hides the class-specific swap from name lookup other than ADL). |
| 79 | + |
| 80 | +## Notes |
| 81 | + |
| 82 | +Notes |
| 83 | +It is unspecified whether <RevisionBlock until="C++11" noborder> <CppHeader name="algorithm" /> </RevisionBlock> |
| 84 | +<RevisionBlock since="C++11" noborder> <CppHeader name="utility" /> </RevisionBlock> is actually included when the standard library functions perform the swap, so the user-provided swap() should not expect it to be included. |
| 85 | + |
| 86 | + |
| 87 | +## Example |
| 88 | + |
| 89 | +```c++ |
| 90 | +#include <iostream> |
| 91 | +#include <vector> |
| 92 | + |
| 93 | +struct IntVector |
| 94 | +{ |
| 95 | + std::vector<int> v; |
| 96 | + |
| 97 | + IntVector& operator=(IntVector) = delete; // not assignable |
| 98 | + |
| 99 | + void swap(IntVector& other) |
| 100 | + { |
| 101 | + v.swap(other.v); |
| 102 | + } |
| 103 | + |
| 104 | + void operator()(auto rem, auto term = " ") |
| 105 | + { |
| 106 | + std::cout << rem << "{{"; |
| 107 | + for (int n{}; int e : v) |
| 108 | + std::cout << (n++ ? ", " : "") << e; |
| 109 | + std::cout << "}}" << term; |
| 110 | + } |
| 111 | +}; |
| 112 | + |
| 113 | +void swap(IntVector& v1, IntVector& v2) |
| 114 | +{ |
| 115 | + v1.swap(v2); |
| 116 | +} |
| 117 | + |
| 118 | +int main() |
| 119 | +{ |
| 120 | + IntVector v1{{1, 1, 1, 1}}, v2{{2222, 2222}}; |
| 121 | + |
| 122 | + auto prn = [&]{ v1("v1", ", "), v2("v2", ";\n"); }; |
| 123 | + |
| 124 | +// std::swap(v1, v2); // Compiler error! std::swap requires MoveAssignable |
| 125 | + prn(); |
| 126 | + std::iter_swap(&v1, &v2); // OK: library calls unqualified swap() |
| 127 | + prn(); |
| 128 | + std::ranges::swap(v1, v2); // OK: library calls unqualified swap() |
| 129 | + prn(); |
| 130 | +} |
| 131 | +``` |
| 132 | +
|
| 133 | +Output: |
| 134 | +
|
| 135 | +``` |
| 136 | +v1{{1, 1, 1, 1}}, v2{{2222, 2222}}; |
| 137 | +v1{{2222, 2222}}, v2{{1, 1, 1, 1}}; |
| 138 | +v1{{1, 1, 1, 1}}, v2{{2222, 2222}}; |
| 139 | +``` |
| 140 | +
|
| 141 | +## Defect reports |
| 142 | +
|
| 143 | +The following behavior-changing defect reports were applied retroactively to previously published C++ standards. |
| 144 | +<DRList> |
| 145 | + <DR kind="lwg" id={226} std="C++98"> |
| 146 | + <Fragment slot="behavior-published"> |
| 147 | + it was unclear how the standard library uses swap |
| 148 | + </Fragment> |
| 149 | + <Fragment slot="correct-behavior"> |
| 150 | + clarified to use both `std::` and ADL-found swap |
| 151 | + </Fragment> |
| 152 | + </DR> |
| 153 | +</DRList> |
| 154 | +## See also |
| 155 | +
|
| 156 | +<DescList> |
| 157 | + <Desc kind="class template"> |
| 158 | + <Fragment slot="item"> |
| 159 | + <RevisionBlock noborder since="C++17" > |
| 160 | + <Missing> `std::is_swappable_with` </Missing> |
| 161 | + </RevisionBlock> |
| 162 | + <RevisionBlock noborder since="C++17" > |
| 163 | + <Missing> `std::is_swappable` </Missing> |
| 164 | + </RevisionBlock> |
| 165 | + <RevisionBlock noborder since="C++17" > |
| 166 | + <Missing> `std::is_nothrow_swappable_with` </Missing> |
| 167 | + </RevisionBlock> |
| 168 | + <RevisionBlock noborder since="C++17" > |
| 169 | + <Missing> `std::is_nothrow_swappable` </Missing> |
| 170 | + </RevisionBlock> |
| 171 | + </Fragment> |
| 172 | + checks if objects of a type can be swapped with objects of same or different type |
| 173 | + </Desc> |
| 174 | + <Desc kind="concept"> |
| 175 | + <Fragment slot="item"> |
| 176 | + <RevisionBlock noborder since="C++20"> |
| 177 | + <Missing> `swappable` </Missing> <br/> |
| 178 | + <Missing> `swappable_with` </Missing> |
| 179 | + </RevisionBlock> |
| 180 | + </Fragment> |
| 181 | + specifies that a type can be swapped or that two types can be swapped with each other |
| 182 | + </Desc> |
| 183 | +</DescList> |
| 184 | +
|
0 commit comments