add a range trait to check swappability for range elements

master
Daniel Kolesa 2017-04-14 15:54:15 +02:00
parent c6697c9ff4
commit dbbef8c7ac
2 changed files with 57 additions and 7 deletions

View File

@ -23,8 +23,8 @@ namespace ostd {
* Given a predicate `pred`, this rearranges the range so that items for
* which the predicate returns true are in the first part of the range.
*
* The range must be at least ostd::forward_range_tag. The items are swapped,
* which means the range's reference type must be swappable.
* The range must be at least ostd::forward_range_tag. The range must also
* meet the conditions of ostd::is_range_element_swappable.
*
* The predicate is applied `N` times and the swap is done at most `N` times.
*
@ -32,6 +32,10 @@ namespace ostd {
*/
template<typename R, typename U>
inline R partition(R range, U pred) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
R ret = range;
for (; !range.empty(); range.pop_front()) {
if (pred(range.front())) {
@ -183,8 +187,8 @@ namespace detail {
* comparison function takes two `ostd::range_reference_t<R>` and must
* return a boolean equivalent to `a < b` for ascending order.
*
* The items are swapped in the range, which means the range's reference
* type must be swappable.
* The items are swapped in the range, which means the range must also
* meet the conditions of ostd::is_range_element_swappable.
*
* The worst-case and average performance of this algorithm os `O(n log n)`.
* The best-case performance is `O(n)`. This happens when the range is small
@ -197,6 +201,10 @@ namespace detail {
*/
template<typename R, typename C>
inline R sort_cmp(R range, C compare) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
detail::introsort(range, compare);
return range;
}
@ -215,6 +223,10 @@ inline auto sort_cmp(C &&compare) {
/** @brief Like ostd::sort_cmp() using `std::less<ostd::range_value_t<R>>{}`. */
template<typename R>
inline R sort(R range) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
return sort_cmp(range, std::less<range_value_t<R>>{});
}

View File

@ -168,7 +168,9 @@ namespace detail {
constexpr bool test_range_category = range_category_test<R>::value;
template<typename R, bool>
struct range_traits_base {};
struct range_traits_base {
static constexpr bool is_element_swappable = false;
};
template<typename R>
struct range_traits_base<R, true> {
@ -177,10 +179,18 @@ namespace detail {
using value_type = typename R::value_type;
using reference = typename R::reference;
using difference_type = typename R::difference_type;
static constexpr bool is_element_swappable =
std::is_convertible_v<range_category, input_range_tag> &&
std::is_lvalue_reference_v<reference> &&
std::is_same_v<value_type, std::remove_reference_t<reference>> &&
std::is_swappable_v<value_type>;
};
template<typename R, bool>
struct range_traits_impl {};
struct range_traits_impl {
static constexpr bool is_element_swappable = false;
};
template<typename R>
struct range_traits_impl<R, true>: range_traits_base<
@ -194,7 +204,7 @@ namespace detail {
*
* Using range traits, you can for check various properties of the range.
* If the provided `R` type is not a range type, the traits struct will
* be empty. Otherwise, it will contain the following:
* be empty. Otherwise, it will contain the following member types:
*
* * `range_category` - one of the category tags (see ostd::input_range_tag)
* * `size_type` - can contain the range's length (typically `size_t`)
@ -202,6 +212,22 @@ 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:
*
* ~~~{.cc}
* static constexpr bool is_element_swappable = ...;
* ~~~
*
* This member will be false for non-range types and otherwise true when
* the following conditions are met:
*
* * The range is an ostd::input_range_tag or better (not an output range).
* * The `reference` member type is an lvalue 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>`).
*
* If any of these four is not met, the member will also be false.
*
* You can read about more details [here](@ref ranges).
*
* @see ostd::range_category_t, ostd::range_size_t, ostd::range_value_t,
@ -280,6 +306,18 @@ using range_reference_t = typename range_traits<R>::reference;
template<typename R>
using range_difference_t = typename range_traits<R>::difference_type;
/** @brief Checks whether the range's element accessors allow swapping.
*
* It's the same as doing
*
* ~~~{.cc}
* ostd::range_traits<R>::is_element_swappable
* ~~~
*
*/
template<typename R>
constexpr bool is_range_element_swappable = range_traits<R>::is_element_swappable;
// is input range
namespace detail {