From cd3ccba7570e839c3752ab8f9f4b95b9169b8c9d Mon Sep 17 00:00:00 2001 From: q66 Date: Sat, 15 Apr 2017 14:20:01 +0200 Subject: [PATCH] more elaborate range element swap tests --- ostd/algorithm.hh | 17 ++----- ostd/range.hh | 114 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 27 deletions(-) diff --git a/ostd/algorithm.hh b/ostd/algorithm.hh index 703bf7b..97938b6 100644 --- a/ostd/algorithm.hh +++ b/ostd/algorithm.hh @@ -1023,27 +1023,18 @@ inline void generate(R range, F gen) { /** @brief Swaps the contents of two input ranges. * - * Both ranges must meet the conditions of ostd::is_range_element_swappable - * and the value types of both ranges must also be the same. The ranges must - * be at least ostd::input_range_tag. The swapping stops as soon as any of - * the ranges becomes empty. + * Testing ostd::is_range_element_swappable_with must be true with the given + * ranges. The ranges must be at least ostd::input_range_tag. The swapping + * stops as soon as any of the ranges becomes empty. * * @returns The `range1` and `range2` in a pair after swapping is done. */ template inline std::pair swap_ranges(R1 range1, R2 range2) { static_assert( - is_range_element_swappable, + is_range_element_swappable_with, "The range element accessors must allow swapping" ); - static_assert( - is_range_element_swappable, - "The range element accessors must allow swapping" - ); - static_assert( - std::is_same_v, range_value_t>, - "The range value types must be the same" - ); while (!range1.empty() && !range2.empty()) { using std::swap; swap(range1.front(), range2.front()); diff --git a/ostd/range.hh b/ostd/range.hh index fba779c..d25715d 100644 --- a/ostd/range.hh +++ b/ostd/range.hh @@ -169,9 +169,23 @@ namespace detail { template struct range_traits_base { + template + static constexpr bool is_element_swappable_with = false; static constexpr bool is_element_swappable = false; + template + static constexpr bool is_element_nothrow_swappable_with = false; + static constexpr bool is_element_nothrow_swappable = false; }; + template + constexpr bool test_elem_reference = + std::is_convertible_v && + std::is_lvalue_reference_v && + std::is_same_v< + typename R::value_type, + std::remove_reference_t + >; + template struct range_traits_base { using range_category = typename R::range_category; @@ -180,16 +194,31 @@ namespace detail { using reference = typename R::reference; using difference_type = typename R::difference_type; + template + static constexpr bool is_element_swappable_with = + test_elem_reference && test_elem_reference && + std::is_swappable_with_v; + static constexpr bool is_element_swappable = - std::is_convertible_v && - std::is_lvalue_reference_v && - std::is_same_v> && - std::is_swappable_v; + is_element_swappable_with; + + template + static constexpr bool is_element_nothrow_swappable_with = + test_elem_reference && test_elem_reference && + std::is_nothrow_swappable_with_v; + + static constexpr bool is_element_nothrow_swappable = + is_element_nothrow_swappable_with; }; template struct range_traits_impl { + template + static constexpr bool is_element_swappable_with = false; static constexpr bool is_element_swappable = false; + template + static constexpr bool is_element_nothrow_swappable_with = false; + static constexpr bool is_element_nothrow_swappable = false; }; template @@ -212,21 +241,38 @@ namespace detail { * * `reference` - the type returned from value accessors * * `difference_type` - the type used for distances (typically `ptrdiff_t`) * - * It will always contain the following member as well: + * It will always contain the following members as well: * * ~~~{.cc} - * static constexpr bool is_element_swappable = ...; + * template + * static constexpr bool is_element_swappable_with = ...; + * static constexpr bool is_element_swappable = is_element_swappable_with; + * template + * static constexpr bool is_element_nothrow_swappable_with = ...; + * static constexpr bool is_element_nothrow_swappable = is_element_swappable_with; * ~~~ * - * This member will be false for non-range types and otherwise true when - * the following conditions are met: + * These members will be false for non-range types. Otherwise, the first + * member tests for the following: * - * * The range is an ostd::input_range_tag or better (not an output range). - * * The `reference` member type is an lvalue reference. + * * Both ranges are an ostd::input_range_tag or better (not an output range). + * * The `reference` member type of both ranges is an lvalue reference. * * The `value_type` is the same as `std::remove_reference_t`. - * * The `value_type` member is swappable (`std::is_swappable_v`). + * * The `reference` types of both ranges are swappable with each other. * - * If any of these four is not met, the member will also be false. + * The references are swappable when the following is true: + * + * ~~~{.cc} + * std::is_swappable_with_v> + * ~~~ + * + * The second member simply tests if the range's own references are swappable + * with each other. without there being another range to swap with. + * + * The nothrow versions are equivalent, except they use the nothrow versions + * of standard swappable test traits (std::is_nothrow_swappable_with_v). + * + * If any of these conditions fail, the members will be false. * * You can read about more details [here](@ref ranges). * @@ -306,7 +352,20 @@ using range_reference_t = typename range_traits::reference; template using range_difference_t = typename range_traits::difference_type; -/** @brief Checks whether the range's element accessors allow swapping. +/** @brief Checks whether two ranges can swap elements with each other. + * + * It's the same as doing + * + * ~~~{.cc} + * ostd::range_traits::template is_element_swappable_with + * ~~~ + * + */ +template +constexpr bool is_range_element_swappable_with = + range_traits::template is_element_swappable_with; + +/** @brief Checks whether a range can swap elements within itself. * * It's the same as doing * @@ -316,7 +375,34 @@ using range_difference_t = typename range_traits::difference_type; * */ template -constexpr bool is_range_element_swappable = range_traits::is_element_swappable; +constexpr bool is_range_element_swappable = + range_traits::is_element_swappable; + +/** @brief Checks whether two ranges can nothrow swap elements with each other. + * + * It's the same as doing + * + * ~~~{.cc} + * ostd::range_traits::template is_element_nothrow_swappable_with + * ~~~ + * + */ +template +constexpr bool is_range_element_nothrow_swappable_with = + range_traits::template is_element_nothrow_swappable_with; + +/** @brief Checks whether a range can nothrow swap elements within itself. + * + * It's the same as doing + * + * ~~~{.cc} + * ostd::range_traits::is_element_nothrow_swappable + * ~~~ + * + */ +template +constexpr bool is_range_element_nothrow_swappable = + range_traits::is_element_nothrow_swappable; // is input range