more elaborate range element swap tests

master
Daniel Kolesa 2017-04-15 14:20:01 +02:00
parent 45b5eb2c0e
commit cd3ccba757
2 changed files with 104 additions and 27 deletions

View File

@ -1023,27 +1023,18 @@ inline void generate(R range, F gen) {
/** @brief Swaps the contents of two input ranges. /** @brief Swaps the contents of two input ranges.
* *
* Both ranges must meet the conditions of ostd::is_range_element_swappable * Testing ostd::is_range_element_swappable_with must be true with the given
* and the value types of both ranges must also be the same. The ranges must * ranges. The ranges must be at least ostd::input_range_tag. The swapping
* be at least ostd::input_range_tag. The swapping stops as soon as any of * stops as soon as any of the ranges becomes empty.
* the ranges becomes empty.
* *
* @returns The `range1` and `range2` in a pair after swapping is done. * @returns The `range1` and `range2` in a pair after swapping is done.
*/ */
template<typename R1, typename R2> template<typename R1, typename R2>
inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) { inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) {
static_assert( static_assert(
is_range_element_swappable<R1>, is_range_element_swappable_with<R1, R2>,
"The range element accessors must allow swapping" "The range element accessors must allow swapping"
); );
static_assert(
is_range_element_swappable<R2>,
"The range element accessors must allow swapping"
);
static_assert(
std::is_same_v<range_value_t<R1>, range_value_t<R2>>,
"The range value types must be the same"
);
while (!range1.empty() && !range2.empty()) { while (!range1.empty() && !range2.empty()) {
using std::swap; using std::swap;
swap(range1.front(), range2.front()); swap(range1.front(), range2.front());

View File

@ -169,9 +169,23 @@ namespace detail {
template<typename R, bool> template<typename R, bool>
struct range_traits_base { struct range_traits_base {
template<typename>
static constexpr bool is_element_swappable_with = false;
static constexpr bool is_element_swappable = false; static constexpr bool is_element_swappable = false;
template<typename>
static constexpr bool is_element_nothrow_swappable_with = false;
static constexpr bool is_element_nothrow_swappable = false;
}; };
template<typename R>
constexpr bool test_elem_reference =
std::is_convertible_v<typename R::range_category, input_range_tag> &&
std::is_lvalue_reference_v<typename R::reference> &&
std::is_same_v<
typename R::value_type,
std::remove_reference_t<typename R::reference>
>;
template<typename R> template<typename R>
struct range_traits_base<R, true> { struct range_traits_base<R, true> {
using range_category = typename R::range_category; using range_category = typename R::range_category;
@ -180,16 +194,31 @@ namespace detail {
using reference = typename R::reference; using reference = typename R::reference;
using difference_type = typename R::difference_type; using difference_type = typename R::difference_type;
template<typename R2>
static constexpr bool is_element_swappable_with =
test_elem_reference<R> && test_elem_reference<R2> &&
std::is_swappable_with_v<reference, typename R2::reference>;
static constexpr bool is_element_swappable = static constexpr bool is_element_swappable =
std::is_convertible_v<range_category, input_range_tag> && is_element_swappable_with<R>;
std::is_lvalue_reference_v<reference> &&
std::is_same_v<value_type, std::remove_reference_t<reference>> && template<typename R2>
std::is_swappable_v<value_type>; static constexpr bool is_element_nothrow_swappable_with =
test_elem_reference<R> && test_elem_reference<R2> &&
std::is_nothrow_swappable_with_v<reference, typename R2::reference>;
static constexpr bool is_element_nothrow_swappable =
is_element_nothrow_swappable_with<R>;
}; };
template<typename R, bool> template<typename R, bool>
struct range_traits_impl { struct range_traits_impl {
template<typename>
static constexpr bool is_element_swappable_with = false;
static constexpr bool is_element_swappable = false; static constexpr bool is_element_swappable = false;
template<typename>
static constexpr bool is_element_nothrow_swappable_with = false;
static constexpr bool is_element_nothrow_swappable = false;
}; };
template<typename R> template<typename R>
@ -212,21 +241,38 @@ namespace detail {
* * `reference` - the type returned from value accessors * * `reference` - the type returned from value accessors
* * `difference_type` - the type used for distances (typically `ptrdiff_t`) * * `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} * ~~~{.cc}
* static constexpr bool is_element_swappable = ...; * template<typename R2>
* static constexpr bool is_element_swappable_with = ...;
* static constexpr bool is_element_swappable = is_element_swappable_with<R, R2>;
* template<typename R2>
* static constexpr bool is_element_nothrow_swappable_with = ...;
* static constexpr bool is_element_nothrow_swappable = is_element_swappable_with<R, R2>;
* ~~~ * ~~~
* *
* This member will be false for non-range types and otherwise true when * These members will be false for non-range types. Otherwise, the first
* the following conditions are met: * member tests for the following:
* *
* * The range is an ostd::input_range_tag or better (not an output range). * * Both ranges are an ostd::input_range_tag or better (not an output range).
* * The `reference` member type is an lvalue reference. * * The `reference` member type of both ranges is an lvalue reference.
* * The `value_type` is the same as `std::remove_reference_t<reference>`. * * The `value_type` is the same as `std::remove_reference_t<reference>`.
* * The `value_type` member is swappable (`std::is_swappable_v<value_type>`). * * 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<reference, ostd::range_reference_t<R2>>
* ~~~
*
* 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). * You can read about more details [here](@ref ranges).
* *
@ -306,7 +352,20 @@ using range_reference_t = typename range_traits<R>::reference;
template<typename R> template<typename R>
using range_difference_t = typename range_traits<R>::difference_type; using range_difference_t = typename range_traits<R>::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<R1>::template is_element_swappable_with<R2>
* ~~~
*
*/
template<typename R1, typename R2>
constexpr bool is_range_element_swappable_with =
range_traits<R1>::template is_element_swappable_with<R2>;
/** @brief Checks whether a range can swap elements within itself.
* *
* It's the same as doing * It's the same as doing
* *
@ -316,7 +375,34 @@ using range_difference_t = typename range_traits<R>::difference_type;
* *
*/ */
template<typename R> template<typename R>
constexpr bool is_range_element_swappable = range_traits<R>::is_element_swappable; constexpr bool is_range_element_swappable =
range_traits<R>::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<R1>::template is_element_nothrow_swappable_with<R2>
* ~~~
*
*/
template<typename R1, typename R2>
constexpr bool is_range_element_nothrow_swappable_with =
range_traits<R1>::template is_element_nothrow_swappable_with<R2>;
/** @brief Checks whether a range can nothrow swap elements within itself.
*
* It's the same as doing
*
* ~~~{.cc}
* ostd::range_traits<R>::is_element_nothrow_swappable
* ~~~
*
*/
template<typename R>
constexpr bool is_range_element_nothrow_swappable =
range_traits<R>::is_element_nothrow_swappable;
// is input range // is input range