зеркало из https://github.com/microsoft/STL.git
Implement ranges::merge (#1101)
This commit is contained in:
Родитель
0eb754dedd
Коммит
5d00b606b4
|
@ -7089,6 +7089,90 @@ _FwdIt3 merge(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2
|
|||
_REQUIRE_PARALLEL_ITERATOR(_FwdIt3);
|
||||
return _STD merge(_First1, _Last1, _First2, _Last2, _Dest);
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_concepts
|
||||
namespace ranges {
|
||||
// ALIAS TEMPLATE merge_result
|
||||
template <class _In1, class _In2, class _Out>
|
||||
using merge_result = in_in_out_result<_In1, _In2, _Out>;
|
||||
|
||||
// VARIABLE ranges::merge
|
||||
class _Merge_fn : private _Not_quite_object {
|
||||
public:
|
||||
using _Not_quite_object::_Not_quite_object;
|
||||
|
||||
// clang-format off
|
||||
template <input_iterator _It1, sentinel_for<_It1> _Se1, input_iterator _It2, sentinel_for<_It2> _Se2,
|
||||
weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity>
|
||||
requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>
|
||||
constexpr merge_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2,
|
||||
_Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
|
||||
// clang-format on
|
||||
_Adl_verify_range(_First1, _Last1);
|
||||
_Adl_verify_range(_First2, _Last2);
|
||||
auto _UResult = _Merge_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)),
|
||||
_Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), _STD move(_Result),
|
||||
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
|
||||
_Seek_wrapped(_First1, _STD move(_UResult.in1));
|
||||
_Seek_wrapped(_First2, _STD move(_UResult.in2));
|
||||
return {_STD move(_First1), _STD move(_First2), _STD move(_UResult.out)};
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
template <input_range _Rng1, input_range _Rng2, weakly_incrementable _Out, class _Pr = ranges::less,
|
||||
class _Pj1 = identity, class _Pj2 = identity>
|
||||
requires mergeable<iterator_t<_Rng1>, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2>
|
||||
constexpr merge_result<borrowed_iterator_t<_Rng1>, borrowed_iterator_t<_Rng2>, _Out> operator()(
|
||||
_Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
|
||||
// clang-format on
|
||||
auto _First1 = _RANGES begin(_Range1);
|
||||
auto _First2 = _RANGES begin(_Range2);
|
||||
auto _UResult =
|
||||
_Merge_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Get_unwrapped(_STD move(_First2)),
|
||||
_Uend(_Range2), _STD move(_Result), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
|
||||
_Seek_wrapped(_First1, _STD move(_UResult.in1));
|
||||
_Seek_wrapped(_First2, _STD move(_UResult.in2));
|
||||
return {_STD move(_First1), _STD move(_First2), _STD move(_UResult.out)};
|
||||
}
|
||||
|
||||
private:
|
||||
template <class _It1, class _Se1, class _It2, class _Se2, class _Out, class _Pr, class _Pj1, class _Pj2>
|
||||
_NODISCARD static constexpr merge_result<_It1, _It2, _Out> _Merge_unchecked(_It1 _First1, const _Se1 _Last1,
|
||||
_It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
|
||||
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>);
|
||||
_STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>);
|
||||
|
||||
for (;; ++_Result) {
|
||||
if (_First1 == _Last1) {
|
||||
auto _Copy_result =
|
||||
_RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_Result));
|
||||
return {_STD move(_First1), _STD move(_Copy_result.in), _STD move(_Copy_result.out)};
|
||||
}
|
||||
|
||||
if (_First2 == _Last2) {
|
||||
auto _Copy_result =
|
||||
_RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result));
|
||||
return {_STD move(_Copy_result.in), _STD move(_First2), _STD move(_Copy_result.out)};
|
||||
}
|
||||
|
||||
if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) {
|
||||
*_Result = *_First2;
|
||||
++_First2;
|
||||
} else {
|
||||
*_Result = *_First1;
|
||||
++_First1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Merge_fn merge{_Not_quite_object::_Construct_tag{}};
|
||||
} // namespace ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
#endif // _HAS_CXX17
|
||||
|
||||
// FUNCTION TEMPLATE inplace_merge
|
||||
|
|
|
@ -265,6 +265,7 @@ tests\P0896R4_ranges_alg_heap
|
|||
tests\P0896R4_ranges_alg_includes
|
||||
tests\P0896R4_ranges_alg_is_permutation
|
||||
tests\P0896R4_ranges_alg_is_sorted
|
||||
tests\P0896R4_ranges_alg_merge
|
||||
tests\P0896R4_ranges_alg_minmax
|
||||
tests\P0896R4_ranges_alg_mismatch
|
||||
tests\P0896R4_ranges_alg_move
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\concepts_matrix.lst
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
#include <range_algorithm_support.hpp>
|
||||
|
||||
using namespace std;
|
||||
using P = pair<int, int>;
|
||||
|
||||
// Validate that merge_result aliases in_in_out_result
|
||||
STATIC_ASSERT(same_as<ranges::merge_result<int, void*, double>, ranges::in_in_out_result<int, void*, double>>);
|
||||
|
||||
// Validate dangling story
|
||||
STATIC_ASSERT(same_as<decltype(ranges::merge(borrowed<false>{}, borrowed<false>{}, nullptr_to<int>)),
|
||||
ranges::merge_result<ranges::dangling, ranges::dangling, int*>>);
|
||||
STATIC_ASSERT(same_as<decltype(ranges::merge(borrowed<false>{}, borrowed<true>{}, nullptr_to<int>)),
|
||||
ranges::merge_result<ranges::dangling, int*, int*>>);
|
||||
STATIC_ASSERT(same_as<decltype(ranges::merge(borrowed<true>{}, borrowed<false>{}, nullptr_to<int>)),
|
||||
ranges::merge_result<int*, ranges::dangling, int*>>);
|
||||
STATIC_ASSERT(same_as<decltype(ranges::merge(borrowed<true>{}, borrowed<true>{}, nullptr_to<int>)),
|
||||
ranges::merge_result<int*, int*, int*>>);
|
||||
|
||||
struct instantiator {
|
||||
static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}};
|
||||
static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}};
|
||||
static constexpr P expected[] = {
|
||||
{0, 10}, {0, 11}, {0, 12}, {13, 0}, {14, 0}, {1, 10}, {1, 11}, {10, 2}, {3, 10}, {11, 3}, {12, 3}};
|
||||
|
||||
static constexpr auto counting_compare(size_t& counter) {
|
||||
return [&counter](auto&& x, auto&& y) {
|
||||
++counter;
|
||||
return ranges::less{}(x, y);
|
||||
};
|
||||
}
|
||||
|
||||
template <ranges::input_range R1, ranges::input_range R2, weakly_incrementable O>
|
||||
static constexpr void call() {
|
||||
using ranges::merge, ranges::merge_result, ranges::end, ranges::equal, ranges::iterator_t, ranges::size;
|
||||
|
||||
{ // Validate range overload
|
||||
P output[size(expected)]{};
|
||||
R1 range1{elements1};
|
||||
R2 range2{elements2};
|
||||
size_t counter = 0;
|
||||
|
||||
const same_as<merge_result<iterator_t<R1>, iterator_t<R2>, O>> auto result =
|
||||
merge(range1, range2, O{output}, counting_compare(counter), get_first, get_second);
|
||||
assert(result.in1 == range1.end());
|
||||
assert(result.in2 == range2.end());
|
||||
assert(result.out.peek() == end(output));
|
||||
assert(equal(output, expected));
|
||||
assert(counter <= size(elements1) + size(elements2) - 1);
|
||||
}
|
||||
{ // Validate iterator overload
|
||||
P output[size(expected)]{};
|
||||
R1 range1{elements1};
|
||||
R2 range2{elements2};
|
||||
size_t counter = 0;
|
||||
|
||||
const same_as<merge_result<iterator_t<R1>, iterator_t<R2>, O>> auto result =
|
||||
merge(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, counting_compare(counter),
|
||||
get_first, get_second);
|
||||
assert(result.in1 == range1.end());
|
||||
assert(result.in2 == range2.end());
|
||||
assert(result.out.peek() == end(output));
|
||||
assert(equal(output, expected));
|
||||
assert(counter <= size(elements1) + size(elements2) - 1);
|
||||
}
|
||||
|
||||
{ // Validate range overload, empty range1
|
||||
P output[size(elements2)]{};
|
||||
R1 range1{};
|
||||
R2 range2{elements2};
|
||||
size_t counter = 0;
|
||||
|
||||
const same_as<merge_result<iterator_t<R1>, iterator_t<R2>, O>> auto result =
|
||||
merge(range1, range2, O{output}, counting_compare(counter), get_first, get_second);
|
||||
assert(result.in1 == range1.end());
|
||||
assert(result.in2 == range2.end());
|
||||
assert(result.out.peek() == end(output));
|
||||
assert(equal(output, elements2));
|
||||
assert(counter == 0);
|
||||
}
|
||||
{ // Validate iterator overload, empty range2
|
||||
P output[size(elements1)]{};
|
||||
R1 range1{elements1};
|
||||
R2 range2{};
|
||||
size_t counter = 0;
|
||||
|
||||
const same_as<merge_result<iterator_t<R1>, iterator_t<R2>, O>> auto result =
|
||||
merge(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, counting_compare(counter),
|
||||
get_first, get_second);
|
||||
assert(result.in1 == range1.end());
|
||||
assert(result.in2 == range2.end());
|
||||
assert(result.out.peek() == end(output));
|
||||
assert(equal(output, elements1));
|
||||
assert(counter == 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Continuation>
|
||||
struct generate_readable_ranges {
|
||||
template <class... Args>
|
||||
static constexpr void call() {
|
||||
using namespace test;
|
||||
using test::range;
|
||||
|
||||
// The algorithm is completely oblivious to:
|
||||
// * categories stronger than input
|
||||
// * whether the end sentinel is an iterator
|
||||
// * size information
|
||||
// * iterator and/or sentinel differencing
|
||||
// so let's vary proxyness for coverage and call it good.
|
||||
|
||||
Continuation::template call<Args...,
|
||||
range<input, const P, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::no>>();
|
||||
Continuation::template call<Args...,
|
||||
range<input, const P, Sized::no, CanDifference::no, Common::no, CanCompare::no, ProxyRef::yes>>();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Continuation>
|
||||
struct generate_writable_iterators {
|
||||
template <class... Args>
|
||||
static constexpr void call() {
|
||||
using namespace test;
|
||||
using test::iterator;
|
||||
|
||||
// The algorithm is completely oblivious to all properties except for proxyness,
|
||||
// so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm
|
||||
// doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression.
|
||||
|
||||
Continuation::template call<Args..., iterator<output, P, CanDifference::no, CanCompare::no, ProxyRef::no>>();
|
||||
Continuation::template call<Args..., iterator<output, P, CanDifference::no, CanCompare::no, ProxyRef::yes>>();
|
||||
|
||||
Continuation::template call<Args..., iterator<input, P, CanDifference::no, CanCompare::no, ProxyRef::no>>();
|
||||
Continuation::template call<Args..., iterator<input, P, CanDifference::no, CanCompare::no, ProxyRef::yes>>();
|
||||
}
|
||||
};
|
||||
|
||||
constexpr void run_tests() {
|
||||
generate_readable_ranges<generate_readable_ranges<generate_writable_iterators<instantiator>>>::call();
|
||||
}
|
||||
|
||||
int main() {
|
||||
STATIC_ASSERT((run_tests(), true));
|
||||
run_tests();
|
||||
}
|
Загрузка…
Ссылка в новой задаче