/// \file // Concepts library // // Copyright Eric Niebler 2013-present // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 #ifndef CPP_SWAP_HPP #define CPP_SWAP_HPP #include #include #include #include // Note: constexpr implies inline, to retain the same visibility // C++14 constexpr functions are inline in C++11 #if (defined(__cpp_constexpr) && __cpp_constexpr >= 201304L) ||\ (!defined(__cpp_constexpr) && __cplusplus >= 201402L) #define CPP_CXX14_CONSTEXPR constexpr #else #define CPP_CXX14_CONSTEXPR inline #endif #ifndef CPP_CXX_INLINE_VARIABLES #ifdef __cpp_inline_variables // TODO: fix this if SD-6 picks another name #define CPP_CXX_INLINE_VARIABLES __cpp_inline_variables // TODO: remove once clang defines __cpp_inline_variables (or equivalent) #elif defined(__clang__) && \ (__clang_major__ > 3 || __clang_major__ == 3 && __clang_minor__ == 9) && \ __cplusplus > 201402L #define CPP_CXX_INLINE_VARIABLES 201606L #else #define CPP_CXX_INLINE_VARIABLES __cplusplus #endif // __cpp_inline_variables #endif // CPP_CXX_INLINE_VARIABLES #if defined(_MSC_VER) && !defined(__clang__) #define CPP_WORKAROUND_MSVC_895622 // Error when phase 1 name binding finds only deleted function #endif #if CPP_CXX_INLINE_VARIABLES < 201606L #define CPP_INLINE_VAR #define CPP_INLINE_VARIABLE(type, name) \ inline namespace \ { \ constexpr auto &name = ::concepts::detail::static_const::value; \ } \ /**/ #else // CPP_CXX_INLINE_VARIABLES >= 201606L #define CPP_INLINE_VAR inline #define CPP_INLINE_VARIABLE(type, name) \ inline constexpr type name{}; \ /**/ #endif // CPP_CXX_INLINE_VARIABLES #if CPP_CXX_INLINE_VARIABLES < 201606L #define CPP_DEFINE_CPO(type, name) \ inline namespace \ { \ constexpr auto &name = ::concepts::detail::static_const::value; \ } \ /**/ #else // CPP_CXX_INLINE_VARIABLES >= 201606L #define CPP_DEFINE_CPO(type, name) \ inline namespace _ \ { \ inline constexpr type name{}; \ } \ /**/ #endif // CPP_CXX_INLINE_VARIABLES #if defined(_MSC_VER) && !defined(__clang__) #define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME #else // ^^^ defined(_MSC_VER) ^^^ / vvv !defined(_MSC_VER) vvv #if defined(__GNUC__) || defined(__clang__) #define CPP_PRAGMA(X) _Pragma(#X) #define CPP_DIAGNOSTIC_IGNORE_PRAGMAS \ CPP_PRAGMA(GCC diagnostic ignored "-Wpragmas") #define CPP_DIAGNOSTIC_IGNORE(X) \ CPP_DIAGNOSTIC_IGNORE_PRAGMAS \ CPP_PRAGMA(GCC diagnostic ignored "-Wunknown-pragmas") \ CPP_PRAGMA(GCC diagnostic ignored X) #define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME \ CPP_DIAGNOSTIC_IGNORE("-Wunknown-warning-option") \ CPP_DIAGNOSTIC_IGNORE("-Winit-list-lifetime") #else #define CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME #endif #endif // MSVC/Generic configuration switch namespace concepts { /// \cond namespace detail { template CPP_INLINE_VAR constexpr bool is_movable_v = std::is_object::value && std::is_move_constructible::value && std::is_move_assignable::value; template struct static_const { static constexpr T const value {}; }; template constexpr T const static_const::value; } /// \endcond template struct is_swappable; template struct is_nothrow_swappable; template struct is_swappable_with; template struct is_nothrow_swappable_with; template CPP_CXX14_CONSTEXPR meta::if_c< std::is_move_constructible::value && std::is_assignable::value, T> exchange(T &t, U &&u) noexcept( std::is_nothrow_move_constructible::value && std::is_nothrow_assignable::value) { T tmp((T &&) t); t = (U &&) u; CPP_DIAGNOSTIC_IGNORE_INIT_LIST_LIFETIME return tmp; } /// \cond namespace adl_swap_detail { struct nope {}; // Intentionally create an ambiguity with std::swap, which is // (possibly) unconstrained. template nope swap(T &, T &) = delete; template nope swap(T (&)[N], T (&)[N]) = delete; #ifdef CPP_WORKAROUND_MSVC_895622 nope swap(); #endif template decltype(swap(std::declval(), std::declval())) try_adl_swap_(int); template nope try_adl_swap_(long); template CPP_INLINE_VAR constexpr bool is_adl_swappable_v = !META_IS_SAME(decltype(adl_swap_detail::try_adl_swap_(42)), nope); struct swap_fn { // Dispatch to user-defined swap found via ADL: template CPP_CXX14_CONSTEXPR meta::if_c> operator()(T &&t, U &&u) const noexcept(noexcept(swap((T &&) t, (U &&) u))) { swap((T &&) t, (U &&) u); } // For intrinsically swappable (i.e., movable) types for which // a swap overload cannot be found via ADL, swap by moving. template CPP_CXX14_CONSTEXPR meta::if_c< !is_adl_swappable_v && detail::is_movable_v> operator()(T &a, T &b) const noexcept(noexcept(b = concepts::exchange(a, (T &&) b))) { b = concepts::exchange(a, (T &&) b); } // For arrays of intrinsically swappable (i.e., movable) types // for which a swap overload cannot be found via ADL, swap array // elements by moving. template CPP_CXX14_CONSTEXPR meta::if_c< !is_adl_swappable_v && is_swappable_with::value> operator()(T (&t)[N], U (&u)[N]) const noexcept(is_nothrow_swappable_with::value) { for(std::size_t i = 0; i < N; ++i) (*this)(t[i], u[i]); } // For rvalue pairs and tuples of swappable types, swap the // members. This permits code like: // ranges::swap(std::tie(a,b,c), std::tie(d,e,f)); template CPP_CXX14_CONSTEXPR meta::if_c::value && is_swappable_with::value> operator()(std::pair &&left, std::pair &&right) const noexcept( is_nothrow_swappable_with::value && is_nothrow_swappable_with::value) { swap_fn()(static_cast &&>(left).first, static_cast &&>(right).first); swap_fn()(static_cast &&>(left).second, static_cast &&>(right).second); } template CPP_CXX14_CONSTEXPR meta::if_c::value...>::value> operator()(std::tuple &&left, std::tuple &&right) const noexcept(meta::and_c::value...>::value) { swap_fn::impl( static_cast &&>(left), static_cast &&>(right), meta::make_index_sequence{}); } private: template static constexpr int ignore_unused(Ts &&...) { return 0; } template CPP_CXX14_CONSTEXPR static void impl(T &&left, U &&right, meta::index_sequence) { (void) swap_fn::ignore_unused( (swap_fn()(std::get(static_cast(left)), std::get(static_cast(right))), 42)...); } }; template struct is_swappable_with_ : std::false_type {}; template struct is_swappable_with_(), std::declval())), decltype(swap_fn()(std::declval(), std::declval()))>> : std::true_type {}; template struct is_nothrow_swappable_with_ : meta::bool_(), std::declval())) && noexcept(swap_fn()(std::declval(), std::declval()))> {}; // Q: Should std::reference_wrapper be considered a proxy wrt swapping rvalues? // A: No. Its operator= is currently defined to reseat the references, so // std::swap(ra, rb) already means something when ra and rb are (lvalue) // reference_wrappers. That reseats the reference wrappers but leaves the // referents unmodified. Treating rvalue reference_wrappers differently would // be confusing. // Q: Then why is it OK to "re"-define swap for pairs and tuples of references? // A: Because as defined above, swapping an rvalue tuple of references has the same // semantics as swapping an lvalue tuple of references. Rather than reseat the // references, assignment happens *through* the references. // Q: But I have an iterator whose operator* returns an rvalue // std::reference_wrapper. How do I make it model indirectly_swappable? // A: With an overload of iter_swap. } /// \endcond /// \ingroup group-utility template struct is_swappable_with : adl_swap_detail::is_swappable_with_ {}; /// \ingroup group-utility template struct is_nothrow_swappable_with : meta::and_< is_swappable_with, adl_swap_detail::is_nothrow_swappable_with_> {}; /// \ingroup group-utility template struct is_swappable : is_swappable_with {}; /// \ingroup group-utility template struct is_nothrow_swappable : is_nothrow_swappable_with {}; /// \ingroup group-utility /// \relates adl_swap_detail::swap_fn CPP_DEFINE_CPO(adl_swap_detail::swap_fn, swap) } #endif