Horizon
Loading...
Searching...
No Matches
swap.hpp
Go to the documentation of this file.
1
2// Concepts library
3//
4// Copyright Eric Niebler 2013-present
5//
6// Use, modification and distribution is subject to the
7// Boost Software License, Version 1.0. (See accompanying
8// file LICENSE_1_0.txt or copy at
9// http://www.boost.org/LICENSE_1_0.txt)
10//
11// Project home: https://github.com/ericniebler/range-v3
12
13#ifndef CPP_SWAP_HPP
14#define CPP_SWAP_HPP
15
16#include <tuple>
17#include <utility>
18#include <type_traits>
19#include <meta/meta.hpp>
20
21// Note: constexpr implies inline, to retain the same visibility
22// C++14 constexpr functions are inline in C++11
23#if (defined(__cpp_constexpr) && __cpp_constexpr >= 201304L) ||\
24 (!defined(__cpp_constexpr) && __cplusplus >= 201402L)
25#define CPP_CXX14_CONSTEXPR constexpr
26#else
27#define CPP_CXX14_CONSTEXPR inline
28#endif
29
30#ifndef CPP_CXX_INLINE_VARIABLES
31#ifdef __cpp_inline_variables // TODO: fix this if SD-6 picks another name
32#define CPP_CXX_INLINE_VARIABLES __cpp_inline_variables
33// TODO: remove once clang defines __cpp_inline_variables (or equivalent)
34#elif defined(__clang__) && \
35 (__clang_major__ > 3 || __clang_major__ == 3 && __clang_minor__ == 9) && \
36 __cplusplus > 201402L
37#define CPP_CXX_INLINE_VARIABLES 201606L
38#else
39#define CPP_CXX_INLINE_VARIABLES __cplusplus
40#endif // __cpp_inline_variables
41#endif // CPP_CXX_INLINE_VARIABLES
42
43#if defined(_MSC_VER) && !defined(__clang__)
44#if _MSC_VER < 1926
45#define CPP_WORKAROUND_MSVC_895622 // Error when phase 1 name binding finds only deleted function
46#endif // _MSC_VER < 1926
47#endif // MSVC
48
49#if CPP_CXX_INLINE_VARIABLES < 201606L
50#define CPP_INLINE_VAR
51#define CPP_INLINE_VARIABLE(type, name) \
52 inline namespace \
53 { \
54 constexpr auto &name = ::concepts::detail::static_const<type>::value; \
55 } \
56
57#else // CPP_CXX_INLINE_VARIABLES >= 201606L
58#define CPP_INLINE_VAR inline
59#define CPP_INLINE_VARIABLE(type, name) \
60 inline constexpr type name{}; \
61
62#endif // CPP_CXX_INLINE_VARIABLES
63
64#if CPP_CXX_INLINE_VARIABLES < 201606L
65#define CPP_DEFINE_CPO(type, name) \
66 inline namespace \
67 { \
68 constexpr auto &name = ::concepts::detail::static_const<type>::value; \
69 } \
70
71#else // CPP_CXX_INLINE_VARIABLES >= 201606L
72#define CPP_DEFINE_CPO(type, name) \
73 inline namespace _ \
74 { \
75 inline constexpr type name{}; \
76 } \
77
78#endif // CPP_CXX_INLINE_VARIABLES
79
80#if defined(_MSC_VER) && !defined(__clang__)
81#define CPP_DIAGNOSTIC_PUSH __pragma(warning(push))
82#define CPP_DIAGNOSTIC_POP __pragma(warning(pop))
83#define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME
84#define CPP_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
85#define CPP_DIAGNOSTIC_IGNORE_CPP2A_COMPAT
86#else // ^^^ defined(_MSC_VER) ^^^ / vvv !defined(_MSC_VER) vvv
87#if defined(__GNUC__) || defined(__clang__)
88#define CPP_PRAGMA(X) _Pragma(#X)
89#define CPP_DIAGNOSTIC_PUSH CPP_PRAGMA(GCC diagnostic push)
90#define CPP_DIAGNOSTIC_POP CPP_PRAGMA(GCC diagnostic pop)
91#define CPP_DIAGNOSTIC_IGNORE_PRAGMAS \
92 CPP_PRAGMA(GCC diagnostic ignored "-Wpragmas")
93#define CPP_DIAGNOSTIC_IGNORE(X) \
94 CPP_DIAGNOSTIC_IGNORE_PRAGMAS \
95 CPP_PRAGMA(GCC diagnostic ignored "-Wunknown-pragmas") \
96 CPP_PRAGMA(GCC diagnostic ignored X)
97#define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME \
98 CPP_DIAGNOSTIC_IGNORE("-Wunknown-warning-option") \
99 CPP_DIAGNOSTIC_IGNORE("-Winit-list-lifetime")
100#define CPP_DIAGNOSTIC_IGNORE_FLOAT_EQUAL CPP_DIAGNOSTIC_IGNORE("-Wfloat-equal")
101#define CPP_DIAGNOSTIC_IGNORE_CPP2A_COMPAT CPP_DIAGNOSTIC_IGNORE("-Wc++2a-compat")
102#else
103#define CPP_DIAGNOSTIC_PUSH
104#define CPP_DIAGNOSTIC_POP
105#define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME
106#define CPP_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
107#define CPP_DIAGNOSTIC_IGNORE_CPP2A_COMPAT
108#endif
109#endif // MSVC/Generic configuration switch
110
111namespace concepts
112{
114 namespace detail
115 {
116 template<typename T>
117 CPP_INLINE_VAR constexpr bool is_movable_v =
118 std::is_object<T>::value &&
119 std::is_move_constructible<T>::value &&
120 std::is_move_assignable<T>::value;
121
122 template<typename T>
123 struct static_const
124 {
125 static constexpr T const value {};
126 };
127 template<typename T>
128 constexpr T const static_const<T>::value;
129 }
131
132 template<typename T>
133 struct is_swappable;
134
135 template<typename T>
136 struct is_nothrow_swappable;
137
138 template<typename T, typename U>
139 struct is_swappable_with;
140
141 template<typename T, typename U>
142 struct is_nothrow_swappable_with;
143
144 template<typename T, typename U = T>
145 CPP_CXX14_CONSTEXPR
146 meta::if_c<
147 std::is_move_constructible<T>::value &&
148 std::is_assignable<T &, U>::value, T>
149 exchange(T &t, U &&u)
150 noexcept(
151 std::is_nothrow_move_constructible<T>::value &&
152 std::is_nothrow_assignable<T &, U>::value)
153 {
154 T tmp((T &&) t);
155 t = (U &&) u;
156 CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME
157 return tmp;
158 }
159
161 namespace adl_swap_detail
162 {
163 struct nope
164 {};
165
166 // Intentionally create an ambiguity with std::swap, which is
167 // (possibly) unconstrained.
168 template<typename T>
169 nope swap(T &, T &) = delete;
170
171 template<typename T, std::size_t N>
172 nope swap(T (&)[N], T (&)[N]) = delete;
173
174#ifdef CPP_WORKAROUND_MSVC_895622
175 nope swap();
176#endif
177
178 template<typename T, typename U>
179 decltype(swap(std::declval<T>(), std::declval<U>())) try_adl_swap_(int);
180
181 template<typename T, typename U>
182 nope try_adl_swap_(long);
183
184 template<typename T, typename U = T>
185 CPP_INLINE_VAR constexpr bool is_adl_swappable_v =
186 !META_IS_SAME(decltype(adl_swap_detail::try_adl_swap_<T, U>(42)), nope);
187
188 struct swap_fn
189 {
190 // Dispatch to user-defined swap found via ADL:
191 template<typename T, typename U>
192 CPP_CXX14_CONSTEXPR
193 meta::if_c<is_adl_swappable_v<T, U>>
194 operator()(T &&t, U &&u) const
195 noexcept(noexcept(swap((T &&) t, (U &&) u)))
196 {
197 swap((T &&) t, (U &&) u);
198 }
199
200 // For intrinsically swappable (i.e., movable) types for which
201 // a swap overload cannot be found via ADL, swap by moving.
202 template<typename T>
203 CPP_CXX14_CONSTEXPR
204 meta::if_c<
205 !is_adl_swappable_v<T &> &&
206 detail::is_movable_v<T>>
207 operator()(T &a, T &b) const
208 noexcept(noexcept(b = concepts::exchange(a, (T &&) b)))
209 {
210 b = concepts::exchange(a, (T &&) b);
211 }
212
213 // For arrays of intrinsically swappable (i.e., movable) types
214 // for which a swap overload cannot be found via ADL, swap array
215 // elements by moving.
216 template<typename T, typename U, std::size_t N>
217 CPP_CXX14_CONSTEXPR
218 meta::if_c<
219 !is_adl_swappable_v<T (&)[N], U (&)[N]> &&
220 is_swappable_with<T &, U &>::value>
221 operator()(T (&t)[N], U (&u)[N]) const
222 noexcept(is_nothrow_swappable_with<T &, U &>::value)
223 {
224 for(std::size_t i = 0; i < N; ++i)
225 (*this)(t[i], u[i]);
226 }
227
228 // For rvalue pairs and tuples of swappable types, swap the
229 // members. This permits code like:
230 // ranges::swap(std::tie(a,b,c), std::tie(d,e,f));
231 template<typename F0, typename S0, typename F1, typename S1>
232 CPP_CXX14_CONSTEXPR
233 meta::if_c<is_swappable_with<F0, F1>::value && is_swappable_with<S0, S1>::value>
234 operator()(std::pair<F0, S0> &&left, std::pair<F1, S1> &&right) const
235 noexcept(
236 is_nothrow_swappable_with<F0, F1>::value &&
237 is_nothrow_swappable_with<S0, S1>::value)
238 {
239 swap_fn()(static_cast<std::pair<F0, S0> &&>(left).first,
240 static_cast<std::pair<F1, S1> &&>(right).first);
241 swap_fn()(static_cast<std::pair<F0, S0> &&>(left).second,
242 static_cast<std::pair<F1, S1> &&>(right).second);
243 }
244
245 template<typename ...Ts, typename ...Us>
246 CPP_CXX14_CONSTEXPR
247 meta::if_c<meta::and_c<is_swappable_with<Ts, Us>::value...>::value>
248 operator()(std::tuple<Ts...> &&left, std::tuple<Us...> &&right) const
250 {
251 swap_fn::impl(
252 static_cast<std::tuple<Ts...> &&>(left),
253 static_cast<std::tuple<Us...> &&>(right),
254 meta::make_index_sequence<sizeof...(Ts)>{});
255 }
256
257 private:
258 template<typename... Ts>
259 static constexpr int ignore_unused(Ts &&...)
260 {
261 return 0;
262 }
263 template<typename T, typename U, std::size_t ...Is>
264 CPP_CXX14_CONSTEXPR
265 static void impl(T &&left, U &&right, meta::index_sequence<Is...>)
266 {
267 (void) swap_fn::ignore_unused(
268 (swap_fn()(std::get<Is>(static_cast<T &&>(left)),
269 std::get<Is>(static_cast<U &&>(right))), 42)...);
270 }
271 };
272
273 template<typename T, typename U, typename = void>
274 struct is_swappable_with_
275 : std::false_type
276 {};
277
278 template<typename T, typename U>
279 struct is_swappable_with_<T, U, meta::void_<
280 decltype(swap_fn()(std::declval<T>(), std::declval<U>())),
281 decltype(swap_fn()(std::declval<U>(), std::declval<T>()))>>
282 : std::true_type
283 {};
284
285 template<typename T, typename U>
286 struct is_nothrow_swappable_with_
287 : meta::bool_<noexcept(swap_fn()(std::declval<T>(), std::declval<U>())) &&
288 noexcept(swap_fn()(std::declval<U>(), std::declval<T>()))>
289 {};
290
291 // Q: Should std::reference_wrapper be considered a proxy wrt swapping rvalues?
292 // A: No. Its operator= is currently defined to reseat the references, so
293 // std::swap(ra, rb) already means something when ra and rb are (lvalue)
294 // reference_wrappers. That reseats the reference wrappers but leaves the
295 // referents unmodified. Treating rvalue reference_wrappers differently would
296 // be confusing.
297
298 // Q: Then why is it OK to "re"-define swap for pairs and tuples of references?
299 // A: Because as defined above, swapping an rvalue tuple of references has the same
300 // semantics as swapping an lvalue tuple of references. Rather than reseat the
301 // references, assignment happens *through* the references.
302
303 // Q: But I have an iterator whose operator* returns an rvalue
304 // std::reference_wrapper<T>. How do I make it model indirectly_swappable?
305 // A: With an overload of iter_swap.
306 }
308
310 template<typename T, typename U>
312 : adl_swap_detail::is_swappable_with_<T, U>
313 {};
314
316 template<typename T, typename U>
318 : meta::and_<
319 is_swappable_with<T, U>,
320 adl_swap_detail::is_nothrow_swappable_with_<T, U>>
321 {};
322
324 template<typename T>
326 : is_swappable_with<T &, T &>
327 {};
328
330 template<typename T>
332 : is_nothrow_swappable_with<T &, T &>
333 {};
334
337 CPP_DEFINE_CPO(adl_swap_detail::swap_fn, swap)
338}
339
340#endif
_t< detail::make_indices_< N, index_sequence< 0 >, detail::strategy_(1, N)> > make_index_sequence
Generate index_sequence containing integer constants [0,1,2,...,N-1].
Definition meta.hpp:473
std::integral_constant< bool, B > bool_
An integral constant wrapper for bool.
Definition meta.hpp:168
_t< defer< detail::_and_< 0==sizeof...(Bs)>::template invoke, Bs... > > and_
Logically AND together all the integral constant-wrapped Boolean parameters, with short-circuiting.
Definition meta.hpp:1411
void void_
An alias for void.
Definition meta.hpp:597
Tiny meta-programming library.
Tiny metaprogramming library.
Definition meta.hpp:116
Definition swap.hpp:333
Definition swap.hpp:313
Definition swap.hpp:327
Definition meta.hpp:1383
A container for a sequence of compile-time integer constants.
Definition meta.hpp:434