зеркало из https://github.com/microsoft/STL.git
<algorithm> Implement P2302R4 ranges::contains (#2911)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net> Co-authored-by: Casey Carter <cacarter@microsoft.com>
This commit is contained in:
Родитель
99740916d6
Коммит
f8a9c6f9b4
|
@ -1364,6 +1364,78 @@ namespace ranges {
|
|||
|
||||
inline constexpr _None_of_fn none_of{_Not_quite_object::_Construct_tag{}};
|
||||
|
||||
#if _HAS_CXX23
|
||||
class _Contains_fn : private _Not_quite_object {
|
||||
public:
|
||||
using _Not_quite_object::_Not_quite_object;
|
||||
|
||||
// clang-format off
|
||||
template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, class _Pj = identity>
|
||||
requires indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty*>
|
||||
_NODISCARD constexpr bool operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const {
|
||||
// clang-format on
|
||||
_Adl_verify_range(_First, _Last);
|
||||
const auto _ULast = _Get_unwrapped(_STD move(_Last));
|
||||
const auto _UResult =
|
||||
_RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _ULast, _Val, _Pass_fn(_Proj));
|
||||
return _UResult != _ULast;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
template <input_range _Rng, class _Ty, class _Pj = identity>
|
||||
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rng>, _Pj>, const _Ty*>
|
||||
_NODISCARD constexpr bool operator()(_Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const {
|
||||
// clang-format on
|
||||
const auto _UResult = _RANGES _Find_unchecked(_Ubegin(_Range), _Uend(_Range), _Val, _Pass_fn(_Proj));
|
||||
return _UResult != _Uend(_Range);
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Contains_fn contains{_Not_quite_object::_Construct_tag{}};
|
||||
|
||||
class _Contains_subrange_fn : private _Not_quite_object {
|
||||
public:
|
||||
using _Not_quite_object::_Not_quite_object;
|
||||
|
||||
// clang-format off
|
||||
template <forward_iterator _It1, sentinel_for<_It1> _Se1, forward_iterator _It2, sentinel_for<_It2> _Se2,
|
||||
class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity>
|
||||
requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
|
||||
_NODISCARD constexpr bool operator()(_It1 _First1,
|
||||
_Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
|
||||
// clang-format on
|
||||
_Adl_verify_range(_First2, _Last2);
|
||||
auto _UFirst2 = _Get_unwrapped(_STD move(_First2));
|
||||
auto _ULast2 = _Get_unwrapped(_STD move(_Last2));
|
||||
|
||||
if (_UFirst2 == _ULast2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto _Match = _RANGES search(_STD move(_First1), _STD move(_Last1), _STD move(_UFirst2),
|
||||
_STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
|
||||
return !_Match.empty();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
template <forward_range _Rng1, forward_range _Rng2, class _Pr = ranges::equal_to, class _Pj1 = identity,
|
||||
class _Pj2 = identity>
|
||||
requires indirectly_comparable<iterator_t<_Rng1>, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2>
|
||||
_NODISCARD constexpr bool operator()(
|
||||
_Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
|
||||
// clang-format on
|
||||
if (_RANGES empty(_Range2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto _Match = _RANGES search(_Range1, _Range2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
|
||||
return !_Match.empty();
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _Contains_subrange_fn contains_subrange{_Not_quite_object::_Construct_tag{}};
|
||||
#endif // _HAS_CXX23
|
||||
|
||||
template <class _In, class _Out>
|
||||
using copy_n_result = in_out_result<_In, _Out>;
|
||||
|
||||
|
|
|
@ -309,6 +309,7 @@
|
|||
// P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr
|
||||
// P2186R2 Removing Garbage Collection Support
|
||||
// P2273R3 constexpr unique_ptr
|
||||
// P2302R4 ranges::contains, ranges::contains_subrange
|
||||
// P2321R2 zip
|
||||
// (changes to pair, tuple, and vector<bool>::reference only)
|
||||
// P2440R1 ranges::iota, ranges::shift_left, ranges::shift_right
|
||||
|
@ -1468,6 +1469,7 @@
|
|||
#define __cpp_lib_out_ptr 202106L
|
||||
#define __cpp_lib_ranges_chunk 202202L
|
||||
#define __cpp_lib_ranges_chunk_by 202202L
|
||||
#define __cpp_lib_ranges_contains 202207L
|
||||
#define __cpp_lib_ranges_iota 202202L
|
||||
#define __cpp_lib_ranges_join_with 202202L
|
||||
#define __cpp_lib_ranges_slide 202202L
|
||||
|
|
|
@ -486,6 +486,8 @@ tests\P2136R3_invoke_r
|
|||
tests\P2162R2_std_visit_for_derived_classes_from_variant
|
||||
tests\P2231R1_complete_constexpr_optional_variant
|
||||
tests\P2273R3_constexpr_unique_ptr
|
||||
tests\P2302R4_ranges_alg_contains
|
||||
tests\P2302R4_ranges_alg_contains_subrange
|
||||
tests\P2321R2_proxy_reference
|
||||
tests\P2401R0_conditional_noexcept_for_exchange
|
||||
tests\P2415R2_owning_view
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\concepts_latest_matrix.lst
|
|
@ -0,0 +1,65 @@
|
|||
// 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;
|
||||
|
||||
struct instantiator {
|
||||
static constexpr pair<int, int> haystack[3] = {{0, 42}, {2, 42}, {4, 42}};
|
||||
|
||||
template <ranges::input_range In>
|
||||
static constexpr void call() {
|
||||
using ranges::contains, ranges::begin, ranges::end;
|
||||
|
||||
for (const auto& [value, _] : haystack) {
|
||||
{ // Validate range overload [found case]
|
||||
const same_as<bool> auto result = contains(In{haystack}, value, get_first);
|
||||
assert(result);
|
||||
}
|
||||
{ // Validate iterator + sentinel overload [found case]
|
||||
const In wrapped{haystack};
|
||||
const same_as<bool> auto result = contains(begin(wrapped), end(wrapped), value, get_first);
|
||||
assert(result);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Validate range overload [not found case]
|
||||
const same_as<bool> auto result = contains(In{haystack}, 42, get_first);
|
||||
assert(!result);
|
||||
}
|
||||
{
|
||||
// Validate iterator + sentinel overload [not found case]
|
||||
const In wrapped{haystack};
|
||||
const same_as<bool> auto result = contains(begin(wrapped), end(wrapped), 42, get_first);
|
||||
assert(!result);
|
||||
}
|
||||
{ // Validate memchr case
|
||||
const char arr[5]{4, 8, 1, -15, 125};
|
||||
|
||||
// found case
|
||||
same_as<bool> auto result = contains(arr, 1);
|
||||
assert(result);
|
||||
|
||||
// not found case
|
||||
result = contains(arr, 10);
|
||||
assert(!result);
|
||||
}
|
||||
{ // unreachable_sentinel case
|
||||
const In wrapped{haystack};
|
||||
const same_as<bool> auto result = contains(begin(wrapped), unreachable_sentinel, 2, get_first);
|
||||
assert(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
STATIC_ASSERT((test_in<instantiator, const pair<int, int>>(), true));
|
||||
test_in<instantiator, const pair<int, int>>();
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\concepts_latest_matrix.lst
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include <range_algorithm_support.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
using Elem1 = const pair<int, int>;
|
||||
using Elem2 = const int;
|
||||
|
||||
struct instantiator {
|
||||
static constexpr pair<int, int> haystack[] = {{0, 42}, {1, 42}, {2, 42}, {3, 42}, {4, 42}, {5, 42}};
|
||||
static constexpr int needle[] = {2, 3, 4};
|
||||
|
||||
template <ranges::forward_range Fwd1, ranges::forward_range Fwd2>
|
||||
static constexpr void call() {
|
||||
using ranges::contains_subrange, ranges::begin, ranges::end;
|
||||
|
||||
{ // Validate range overload [found case]
|
||||
const same_as<bool> auto result =
|
||||
contains_subrange(Fwd1{haystack}, Fwd2{needle}, ranges::equal_to{}, get_first);
|
||||
assert(result);
|
||||
}
|
||||
{ // Validate iterator + sentinel overload [found case]
|
||||
const Fwd1 wrap_hay{haystack};
|
||||
const Fwd2 wrap_needle{needle};
|
||||
const same_as<bool> auto result = contains_subrange(
|
||||
begin(wrap_hay), end(wrap_hay), begin(wrap_needle), end(wrap_needle), ranges::equal_to{}, get_first);
|
||||
assert(result);
|
||||
}
|
||||
{ // Validate range overload [not found case]
|
||||
const same_as<bool> auto result =
|
||||
contains_subrange(Fwd1{haystack}, Fwd2{needle}, ranges::equal_to{}, get_second);
|
||||
assert(!result);
|
||||
}
|
||||
{ // Validate iterator + sentinel overload [not found case]
|
||||
const Fwd1 wrap_hay{haystack};
|
||||
const Fwd2 wrap_needle{needle};
|
||||
const same_as<bool> auto result = contains_subrange(
|
||||
begin(wrap_hay), end(wrap_hay), begin(wrap_needle), end(wrap_needle), ranges::equal_to{}, get_second);
|
||||
assert(!result);
|
||||
}
|
||||
{ // Validate empty needle case
|
||||
const span<Elem1> empty;
|
||||
const same_as<bool> auto result = contains_subrange(Fwd1{haystack}, Fwd1{empty});
|
||||
assert(result);
|
||||
}
|
||||
{ // Validate unreachable_sentinel case
|
||||
const Fwd1 wrap_hay{haystack};
|
||||
const Fwd2 wrap_needle{needle};
|
||||
const same_as<bool> auto result = contains_subrange(begin(wrap_hay), unreachable_sentinel,
|
||||
begin(wrap_needle), end(wrap_needle), ranges::equal_to{}, get_first);
|
||||
assert(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef TEST_EVERYTHING
|
||||
int main() {
|
||||
STATIC_ASSERT((test_fwd_fwd<instantiator, Elem1, Elem2>(), true));
|
||||
test_fwd_fwd<instantiator, Elem1, Elem2>();
|
||||
}
|
||||
#else // ^^^ test all range combinations // test only interesting range combos vvv
|
||||
template <class Elem, test::Sized IsSized>
|
||||
using fwd_test_range = test::range<forward_iterator_tag, Elem, IsSized, test::CanDifference::no, test::Common::no,
|
||||
test::CanCompare::yes, test::ProxyRef::yes>;
|
||||
template <class Elem, test::Sized IsSized, test::Common IsCommon>
|
||||
using random_test_range = test::range<random_access_iterator_tag, Elem, IsSized, test::CanDifference::no, IsCommon,
|
||||
test::CanCompare::yes, test::ProxyRef::no>;
|
||||
|
||||
constexpr bool run_tests() {
|
||||
// All (except contiguous) proxy reference types, since the algorithm doesn't really care. Cases with only 1 range
|
||||
// sized are not interesting; common is interesting only in that it's necessary to trigger memcmp optimization.
|
||||
|
||||
using test::Common, test::Sized;
|
||||
|
||||
// both forward, non-common, and sized or unsized
|
||||
instantiator::call<fwd_test_range<Elem1, Sized::no>, fwd_test_range<Elem2, Sized::no>>();
|
||||
instantiator::call<fwd_test_range<Elem1, Sized::yes>, fwd_test_range<Elem2, Sized::yes>>();
|
||||
|
||||
// both random-access, and sized or unsized; all permutations of common
|
||||
instantiator::call<random_test_range<Elem1, Sized::no, Common::no>,
|
||||
random_test_range<Elem2, Sized::no, Common::no>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::no, Common::no>,
|
||||
random_test_range<Elem2, Sized::no, Common::yes>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::no, Common::yes>,
|
||||
random_test_range<Elem2, Sized::no, Common::no>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::no, Common::yes>,
|
||||
random_test_range<Elem2, Sized::no, Common::yes>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::yes, Common::no>,
|
||||
random_test_range<Elem2, Sized::yes, Common::no>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::yes, Common::no>,
|
||||
random_test_range<Elem2, Sized::yes, Common::yes>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::yes, Common::yes>,
|
||||
random_test_range<Elem2, Sized::yes, Common::no>>();
|
||||
instantiator::call<random_test_range<Elem1, Sized::yes, Common::yes>,
|
||||
random_test_range<Elem2, Sized::yes, Common::yes>>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
STATIC_ASSERT(run_tests());
|
||||
run_tests();
|
||||
}
|
||||
#endif // TEST_EVERYTHING
|
|
@ -1408,6 +1408,20 @@ STATIC_ASSERT(__cpp_lib_ranges_chunk_by == 202202L);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support
|
||||
#ifndef __cpp_lib_ranges_contains
|
||||
#error __cpp_lib_ranges_contains is not defined
|
||||
#elif __cpp_lib_ranges_contains != 202207L
|
||||
#error __cpp_lib_ranges_contains is not 202207L
|
||||
#else
|
||||
STATIC_ASSERT(__cpp_lib_ranges_contains == 202207L);
|
||||
#endif
|
||||
#else
|
||||
#ifdef __cpp_lib_ranges_contains
|
||||
#error __cpp_lib_ranges_contains is defined
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if _HAS_CXX23 && defined(__cpp_lib_concepts)
|
||||
#ifndef __cpp_lib_ranges_iota
|
||||
#error __cpp_lib_ranges_iota is not defined
|
||||
|
|
Загрузка…
Ссылка в новой задаче