P2387R3 Pipe for user defined range adaptors (#2661)

Co-authored-by: nicole mazzuca <mazzucan@outlook.com>
Co-authored-by: Casey Carter <Casey@Carter.net>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Ukilele 2022-08-22 23:05:48 +02:00 коммит произвёл GitHub
Родитель 22d6408f24
Коммит 2ae879b73c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 646 добавлений и 93 удалений

Просмотреть файл

@ -619,6 +619,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
</Expand>
</Type>
<Type Name="std::_Back_binder&lt;*&gt;">
<DisplayString>bind_back({_Mypair}, {_Mypair._Myval2,view(noparens)})</DisplayString>
<Expand>
<Item Name="[f]">_Mypair</Item>
<Item Name="[bound_args]">_Mypair._Myval2</Item>
</Expand>
</Type>
<!-- VC 2015 -->
<Type Name="std::_Mem_fn&lt;*&gt;">

Просмотреть файл

@ -2130,6 +2130,83 @@ _NODISCARD constexpr auto bind_front(_Fx&& _Func, _Types&&... _Args) {
}
#endif // _HAS_CXX20
#if _HAS_CXX23
template <size_t... _Ix, class _Cv_FD, class _Cv_tuple_TiD, class... _Unbound>
constexpr auto _Call_back_binder(index_sequence<_Ix...>, _Cv_FD&& _Obj, _Cv_tuple_TiD&& _Tpl,
_Unbound&&... _Unbargs) noexcept(noexcept(_STD invoke(_STD forward<_Cv_FD>(_Obj),
_STD forward<_Unbound>(_Unbargs)..., _STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...)))
-> decltype(_STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)...,
_STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...)) {
return _STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)...,
_STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...);
}
template <class _Fx, class... _Types>
class _Back_binder { // wrap bound callable object and arguments
private:
using _Seq = index_sequence_for<_Types...>;
_Compressed_pair<_Fx, tuple<_Types...>> _Mypair;
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Fx, decay_t<_Fx>>);
_STL_INTERNAL_STATIC_ASSERT((is_same_v<_Types, decay_t<_Types>> && ...));
public:
template <class _FxInit, class... _TypesInit,
enable_if_t<sizeof...(_TypesInit) != 0 || !is_same_v<remove_cvref_t<_FxInit>, _Back_binder>, int> = 0>
constexpr explicit _Back_binder(_FxInit&& _Func, _TypesInit&&... _Args)
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_FxInit>(_Func), _STD forward<_TypesInit>(_Args)...) {}
template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) & noexcept(
noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...);
}
template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) const& noexcept(
noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...);
}
template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) && noexcept(noexcept(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...);
}
template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) const&& noexcept(noexcept(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...);
}
};
template <class _Fx, class... _Types>
_NODISCARD constexpr auto bind_back(_Fx&& _Func, _Types&&... _Args) {
static_assert(is_constructible_v<decay_t<_Fx>, _Fx>,
"std::bind_back requires the decayed callable to be constructible from an undecayed callable");
static_assert(
is_move_constructible_v<decay_t<_Fx>>, "std::bind_back requires the decayed callable to be move constructible");
static_assert(conjunction_v<is_constructible<decay_t<_Types>, _Types>...>,
"std::bind_back requires the decayed bound arguments to be constructible from undecayed bound arguments");
static_assert(conjunction_v<is_move_constructible<decay_t<_Types>>...>,
"std::bind_back requires the decayed bound arguments to be move constructible");
return _Back_binder<decay_t<_Fx>, decay_t<_Types>...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}
#endif // _HAS_CXX23
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
template <class _Fty, class _Alloc>
struct uses_allocator<function<_Fty>, _Alloc> : true_type {}; // true_type if container allocator enabled

Просмотреть файл

@ -61,108 +61,80 @@ namespace ranges {
using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>;
namespace _Pipe {
// clang-format off
template <class _Left, class _Right>
concept _Can_pipe = requires(_Left&& __l, _Right&& __r) {
static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l));
};
template <class _Left, class _Right>
concept _Can_compose = constructible_from<remove_cvref_t<_Left>, _Left>
&& constructible_from<remove_cvref_t<_Right>, _Right>;
// clang-format on
template <class, class>
struct _Pipeline;
template <class _Derived>
struct _Base {
template <class _Other>
requires _Can_compose<_Derived, _Other>
constexpr auto operator|(_Base<_Other>&& __r) && noexcept(
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) {
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)};
}
struct _Base {};
template <class _Other>
requires _Can_compose<_Derived, const _Other&>
constexpr auto operator|(const _Base<_Other>& __r) && noexcept(
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)})) {
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)};
}
template <class _Ty>
_Ty* _Derived_from_range_adaptor_closure(_Base<_Ty>&); // not defined
template <class _Other>
requires _Can_compose<const _Derived&, _Other>
constexpr auto operator|(_Base<_Other>&& __r) const& noexcept(
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)})) {
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)};
}
template <class _Other>
requires _Can_compose<const _Derived&, const _Other&>
constexpr auto operator|(const _Base<_Other>& __r) const& noexcept(
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)})) {
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)};
}
template <_Can_pipe<const _Derived&> _Left>
friend constexpr auto operator|(_Left&& __l, const _Base& __r)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<const _Derived&>()(_STD forward<_Left>(__l))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l));
}
template <_Can_pipe<_Derived> _Left>
friend constexpr auto operator|(_Left&& __l, _Base&& __r)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Left>(__l))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l));
}
template <class _Ty>
concept _Range_adaptor_closure_object = !range<remove_cvref_t<_Ty>> && requires(remove_cvref_t<_Ty> & __t) {
{ _Pipe::_Derived_from_range_adaptor_closure(__t) } -> same_as<remove_cvref_t<_Ty>*>;
};
template <class _Left, class _Right>
struct _Pipeline : _Base<_Pipeline<_Left, _Right>> {
/* [[no_unique_address]] */ _Left __l;
/* [[no_unique_address]] */ _Right __r;
template <class _ClosureLeft, class _ClosureRight>
struct _Pipeline : _Base<_Pipeline<_ClosureLeft, _ClosureRight>> {
_STL_INTERNAL_STATIC_ASSERT(_Range_adaptor_closure_object<_ClosureLeft>);
_STL_INTERNAL_STATIC_ASSERT(_Range_adaptor_closure_object<_ClosureRight>);
/* [[no_unique_address]] */ _ClosureLeft _Left;
/* [[no_unique_address]] */ _ClosureRight _Right;
template <class _Ty1, class _Ty2>
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>)
: __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {}
is_nothrow_constructible_v<_Ty1, _ClosureLeft>&& is_nothrow_constructible_v<_Ty2, _ClosureRight>)
: _Left(_STD forward<_Ty1>(_Val1)), _Right(_STD forward<_Ty2>(_Val2)) {}
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept(
noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
_NODISCARD constexpr auto operator()(_Ty&& _Val) & noexcept(
noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires {
_Right(_Left(_STD forward<_Ty>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }
{ return _Right(_Left(_STD forward<_Ty>(_Val))); }
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
_NODISCARD constexpr auto operator()(_Ty&& _Val) const& noexcept(
noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires {
_Right(_Left(_STD forward<_Ty>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }
{ return _Right(_Left(_STD forward<_Ty>(_Val))); }
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) && noexcept(
noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires {
_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val)));
}
{ return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); }
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const&& noexcept(
noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires {
_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val)));
}
{ return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); }
};
template <class _Ty1, class _Ty2>
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;
template <class _Left, class _Right>
requires _Range_adaptor_closure_object<_Left> //
&& _Range_adaptor_closure_object<_Right> //
&& constructible_from<remove_cvref_t<_Left>, _Left> //
&& constructible_from<remove_cvref_t<_Right>, _Right> //
_NODISCARD constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept(
noexcept(_Pipeline{static_cast<_Left&&>(__l), static_cast<_Right&&>(__r)})) {
return _Pipeline{static_cast<_Left&&>(__l), static_cast<_Right&&>(__r)};
}
template <class _Left, class _Right>
requires _Range_adaptor_closure_object<_Right> && range<_Left> //
_NODISCARD constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept(
noexcept(_STD forward<_Right>(__r)(_STD forward<_Left>(__l)))) //
requires requires {
static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l));
}
{ return _STD forward<_Right>(__r)(_STD forward<_Left>(__l)); }
} // namespace _Pipe
template <range _Rng, class _Derived>
@ -805,6 +777,13 @@ namespace ranges {
};
};
#if _HAS_CXX23
template <class _Derived>
requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>>
class range_adaptor_closure : public _Pipe::_Base<_Derived> {
};
#endif // _HAS_CXX23
template <class _Fn, class... _Types>
class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> {
public:
@ -4289,7 +4268,6 @@ namespace ranges {
}
}
public:
using iterator_concept = typename _Outer_iter<_Const>::iterator_concept;
using value_type = range_value_t<_BaseTy>;
@ -4506,7 +4484,7 @@ namespace ranges {
#else // ^^^ no workaround / workaround vvv
auto _Match = _RANGES search(subrange{_Current, _Last}, _Parent->_Pattern);
auto _Begin = _Match.begin();
auto _End = _Match.end();
auto _End = _Match.end();
#endif // TRANSITION, DevCom-1559808
if (_Begin != _Last && _RANGES empty(_Parent->_Pattern)) {
++_Begin;
@ -4558,7 +4536,7 @@ namespace ranges {
#else // ^^^ no workaround / workaround vvv
auto _Match = _RANGES search(subrange{_It, _Last}, _Pattern);
auto _Begin = _Match.begin();
auto _End = _Match.end();
auto _End = _Match.end();
#endif // TRANSITION, DevCom-1559808
if (_Begin != _Last && _RANGES empty(_Pattern)) {
++_Begin;
@ -5086,7 +5064,7 @@ namespace ranges {
(void) _Off;
#else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 / _ITERATOR_DEBUG_LEVEL != 0 vvv
if constexpr (_Offset_verifiable_v<iterator_t<_Base>>) {
_Current._Verify_offset(_Off);
_Current._Verify_offset(_Off);
}
#endif // _ITERATOR_DEBUG_LEVEL == 0
}

Просмотреть файл

@ -323,6 +323,7 @@
// P2302R4 ranges::contains, ranges::contains_subrange
// P2321R2 zip
// (changes to pair, tuple, and vector<bool>::reference only)
// P2387R3 Pipe Support For User-Defined Range Adaptors
// P2417R2 More constexpr bitset
// P2440R1 ranges::iota, ranges::shift_left, ranges::shift_right
// P2441R2 views::join_with
@ -1459,10 +1460,6 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_polymorphic_allocator 201902L
#ifdef __cpp_lib_concepts // TRANSITION, GH-395
#define __cpp_lib_ranges 202110L
#endif // __cpp_lib_concepts
#define __cpp_lib_remove_cvref 201711L
#define __cpp_lib_semaphore 201907L
#define __cpp_lib_smart_ptr_for_overwrite 202002L
@ -1495,6 +1492,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#endif // __cpp_lib_concepts
#define __cpp_lib_associative_heterogeneous_erasure 202110L
#define __cpp_lib_bind_back 202202L
#define __cpp_lib_byteswap 202110L
#define __cpp_lib_constexpr_bitset 202207L
#define __cpp_lib_constexpr_typeinfo 202106L
@ -1569,6 +1567,14 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_optional 201606L // P0307R2 Making Optional Greater Equal Again
#endif // _HAS_CXX17
#if defined(__cpp_lib_concepts) // TRANSITION, GH-395
#if _HAS_CXX23
#define __cpp_lib_ranges 202202L // P2387R3 Pipe Support For User-Defined Range Adaptors
#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv
#define __cpp_lib_ranges 202110L // P2415R2 What Is A `view`?
#endif // _HAS_CXX20
#endif // defined(__cpp_lib_concepts)
#if _HAS_CXX20
#define __cpp_lib_shared_ptr_arrays 201707L // P0674R1 make_shared() For Arrays
#else // _HAS_CXX20

Просмотреть файл

@ -543,6 +543,8 @@ tests\P2273R3_constexpr_unique_ptr
tests\P2302R4_ranges_alg_contains
tests\P2302R4_ranges_alg_contains_subrange
tests\P2321R2_proxy_reference
tests\P2387R3_bind_back
tests\P2387R3_pipe_support_for_user_defined_range_adaptors
tests\P2401R0_conditional_noexcept_for_exchange
tests\P2408R5_ranges_iterators_to_classic_algorithms
tests\P2415R2_owning_view

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\usual_latest_matrix.lst

Просмотреть файл

@ -0,0 +1,216 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cassert>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
using namespace std;
constexpr int f0() {
return 1729;
}
constexpr int f1(int x) {
return x * 10;
}
constexpr int f2(int x, int y) {
return x * 100 + y * 10;
}
constexpr int f3(int x, int y, int z) {
return x * 1000 + y * 100 + z * 10;
}
struct Cat {
string name;
};
struct CatNoise {
string noise(const string& s, const Cat& cat) const {
return cat.name + " says " + s;
}
};
struct DetectQualifiers {
constexpr string_view operator()() & {
return "modifiable lvalue";
}
constexpr string_view operator()() const& {
return "const lvalue";
}
constexpr string_view operator()() && {
return "modifiable rvalue";
}
constexpr string_view operator()() const&& {
return "const rvalue";
}
};
constexpr bool test_constexpr() {
// Test varying numbers of arguments.
assert(bind_back(f0)() == 1729);
assert(bind_back(f1)(2) == 20);
assert(bind_back(f1, 3)() == 30);
assert(bind_back(f2)(4, 5) == 450);
assert(bind_back(f2, 7)(6) == 670);
assert(bind_back(f2, 8, 9)() == 890);
assert(bind_back(f3)(2, 3, 4) == 2340);
assert(bind_back(f3, 4, 5)(3) == 3450);
assert(bind_back(f3, 5, 6)(4) == 4560);
assert(bind_back(f3, 5, 6, 7)() == 5670);
// Test function pointers.
assert(bind_back(&f0)() == 1729);
assert(bind_back(&f2, 7)(6) == 670);
// Test stateless lambdas.
assert(bind_back([] { return 11; })() == 11);
assert(bind_back([](int x, int y) { return x * 2 + y * 3; }, 10)(100) == 230);
// Test stateful lambdas.
int value = 0;
auto bound0 = bind_back([&value] { ++value; });
bound0();
assert(value == 1);
bound0();
assert(value == 2);
auto bound1 = bind_back([&value](int x, int y) { value = value * x + y; }, 10);
bound1(3);
assert(value == 16);
bound1(4);
assert(value == 74);
// Test "perfect forwarding call wrapper" behavior.
auto bound5 = bind_back(DetectQualifiers{});
assert(bound5() == "modifiable lvalue");
assert(as_const(bound5)() == "const lvalue");
assert(move(bound5)() == "modifiable rvalue");
assert(move(as_const(bound5))() == "const rvalue");
// Test decay when binding.
const int arr[] = {11, 22, 33};
const int three = 3;
auto bound8 = bind_back(
[](auto&& a, auto&& f, auto&& i) {
using FP = int (*)(int);
return is_same_v<decltype(a), const int*&> && is_same_v<decltype(f), FP&> && is_same_v<decltype(i), int&>;
},
arr, f1, three);
assert(bound8());
// Test forward when calling.
auto bound9 = bind_back([](auto&& a1, auto&& a2, auto&& a3, auto&& a4) {
constexpr bool same1 = is_same_v<decltype(a1), int&>;
constexpr bool same2 = is_same_v<decltype(a2), const int&>;
constexpr bool same3 = is_same_v<decltype(a3), int&&>;
constexpr bool same4 = is_same_v<decltype(a4), const int&&>;
return same1 && same2 && same3 && same4;
});
assert(bound9(value, three, 1729, move(three)));
return true;
}
void test_move_only_types() {
// Test movable-only types.
auto unique_lambda = [up1 = make_unique<int>(1200)](unique_ptr<int>&& up2) {
if (up1 && up2) {
return make_unique<int>(*up1 + *up2);
} else if (up1) {
return make_unique<int>(*up1 * -1);
} else if (up2) {
return make_unique<int>(*up2 * -10);
} else {
return make_unique<int>(-9000);
}
};
auto bound6 = bind_back(move(unique_lambda), make_unique<int>(34));
assert(*unique_lambda(make_unique<int>(56)) == -560);
assert(*move(bound6)() == 1234);
auto bound7 = move(bound6);
assert(*move(bound6)() == -9000);
assert(*move(bound7)() == 1234);
}
int main() {
assert(test_constexpr());
static_assert(test_constexpr());
test_move_only_types();
// Also test GH-1292 "bind_front violates [func.require]p8" in which the return type of bind_front inadvertently
// depends on the value category and/or cv-qualification of its arguments.
{
struct S {
int i = 42;
};
S s;
auto lambda = [](S x) { return x.i; };
auto returns_lambda = [=] { return lambda; };
auto returns_const_lambda = [=]() -> const decltype(lambda) { return lambda; };
auto returns_const_S = []() -> const S { return {}; };
using T = decltype(bind_back(lambda, s));
static_assert(is_same_v<decltype(bind_back(lambda, move(s))), T>);
static_assert(is_same_v<decltype(bind_back(lambda, S{})), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), s)), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), move(s))), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), S{})), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), s)), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), move(s))), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), S{})), T>);
static_assert(is_same_v<decltype(bind_back(lambda, as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(lambda, move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(lambda, returns_const_S())), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(move(lambda), returns_const_S())), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(returns_lambda(), returns_const_S())), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), s)), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), move(s))), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), S{})), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), s)), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), move(s))), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), S{})), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), s)), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), move(s))), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), S{})), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(as_const(lambda), returns_const_S())), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(move(as_const(lambda)), returns_const_S())), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), as_const(s))), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), move(as_const(s)))), T>);
static_assert(is_same_v<decltype(bind_back(returns_const_lambda(), returns_const_S())), T>);
}
}

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst

Просмотреть файл

@ -0,0 +1,236 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <array>
#include <cassert>
#include <ranges>
#include <utility>
using namespace std;
template <class T>
concept CanInstantiateRangeAdaptorClosure = requires {
typename ranges::range_adaptor_closure<T>;
};
class EmptyTestType {};
class IncompleteTestType;
static_assert(CanInstantiateRangeAdaptorClosure<EmptyTestType>);
static_assert(!CanInstantiateRangeAdaptorClosure<const EmptyTestType>);
static_assert(!CanInstantiateRangeAdaptorClosure<volatile EmptyTestType>);
static_assert(!CanInstantiateRangeAdaptorClosure<EmptyTestType&>);
static_assert(!CanInstantiateRangeAdaptorClosure<int>);
static_assert(CanInstantiateRangeAdaptorClosure<IncompleteTestType>);
template <class LHS, class RHS>
concept CanPipe = requires(LHS lhs, RHS rhs) {
forward<LHS>(lhs) | forward<RHS>(rhs);
};
template <class LHS, class RHS, class Ret>
concept CanPipe_R = requires(LHS lhs, RHS rhs) {
{ forward<LHS>(lhs) | forward<RHS>(rhs) } -> same_as<Ret>;
};
using TestRange = array<int, 1>;
template <class T>
constexpr bool is_range_adaptor_closure() {
return CanPipe<TestRange, T&> || CanPipe<TestRange, const T&> //
|| CanPipe<TestRange, T&&> || CanPipe<TestRange, const T&&>;
}
struct IdentityRangeAdaptorClosure : ranges::range_adaptor_closure<IdentityRangeAdaptorClosure> {
template <class T>
constexpr decltype(auto) operator()(T&& range) const {
return forward<T>(range);
}
};
// Is not a range adaptor closure, because it is not a function object.
struct NotCallable : ranges::range_adaptor_closure<NotCallable> {};
static_assert(!is_range_adaptor_closure<NotCallable>());
// Is not a range adaptor closure, because it does not accept a range as argument.
struct NotCallableWithRange : ranges::range_adaptor_closure<NotCallableWithRange> {
void operator()() {}
};
static_assert(!is_range_adaptor_closure<NotCallableWithRange>());
// Is not a range adaptor closure, because it doesn't derive from range_adaptor_closure.
struct NotDerivedFrom {
void operator()(const TestRange&) {}
};
static_assert(!is_range_adaptor_closure<NotDerivedFrom>());
// Is not a range adaptor closure, because it inherits privately from range_adaptor_closure.
struct DerivedPrivately : private ranges::range_adaptor_closure<DerivedPrivately> {
void operator()(const TestRange&) {}
};
static_assert(!is_range_adaptor_closure<DerivedPrivately>());
// Is not a range adaptor closure, because it inherits from the wrong specialization of range_adaptor_closure.
struct DerivedFromWrongSpecialization : ranges::range_adaptor_closure<IdentityRangeAdaptorClosure> {
void operator()(const TestRange&) {}
};
static_assert(!is_range_adaptor_closure<DerivedFromWrongSpecialization>());
// Is not a range adaptor closure, because it has two base classes which are specializations of
// range_adaptor_closure.
struct DerivedFromTwoSpecializations : ranges::range_adaptor_closure<DerivedFromTwoSpecializations>,
ranges::range_adaptor_closure<IdentityRangeAdaptorClosure> {
void operator()(const TestRange&) {}
};
static_assert(!is_range_adaptor_closure<DerivedFromTwoSpecializations>());
// Is not a range adaptor closure, because it models ranges::range.
struct ModelsRange : ranges::range_adaptor_closure<ModelsRange> {
void operator()(const TestRange&) {}
int* begin() {
return nullptr;
}
int* begin() const {
return nullptr;
}
int* end() {
return nullptr;
}
int* end() const {
return nullptr;
}
};
static_assert(ranges::range<ModelsRange>);
static_assert(!is_range_adaptor_closure<ModelsRange>());
struct RangeAdaptorClosureMemberRefQualTest : ranges::range_adaptor_closure<RangeAdaptorClosureMemberRefQualTest> {
constexpr ranges::empty_view<char> operator()(const auto&) & {
return views::empty<char>;
}
constexpr ranges::empty_view<short> operator()(const auto&) && {
return views::empty<short>;
}
constexpr ranges::empty_view<float> operator()(const auto&) const& {
return views::empty<float>;
}
constexpr ranges::empty_view<double> operator()(const auto&) const&& {
return views::empty<double>;
}
};
static_assert(CanPipe_R<TestRange, RangeAdaptorClosureMemberRefQualTest&, ranges::empty_view<char>>);
static_assert(CanPipe_R<TestRange, RangeAdaptorClosureMemberRefQualTest&&, ranges::empty_view<short>>);
static_assert(CanPipe_R<TestRange, RangeAdaptorClosureMemberRefQualTest const&, ranges::empty_view<float>>);
static_assert(CanPipe_R<TestRange, RangeAdaptorClosureMemberRefQualTest const&&, ranges::empty_view<double>>);
using FirstIdentityThenMemberRefQualTest = decltype(
IdentityRangeAdaptorClosure{} | RangeAdaptorClosureMemberRefQualTest{});
static_assert(CanPipe_R<TestRange, FirstIdentityThenMemberRefQualTest&, ranges::empty_view<char>>);
static_assert(CanPipe_R<TestRange, FirstIdentityThenMemberRefQualTest&&, ranges::empty_view<short>>);
static_assert(CanPipe_R<TestRange, FirstIdentityThenMemberRefQualTest const&, ranges::empty_view<float>>);
static_assert(CanPipe_R<TestRange, FirstIdentityThenMemberRefQualTest const&&, ranges::empty_view<double>>);
using FirstTransformThenMemberRefQualTest = decltype(
views::transform([](auto x) { return x; }) | RangeAdaptorClosureMemberRefQualTest{});
static_assert(CanPipe_R<TestRange, FirstTransformThenMemberRefQualTest&, ranges::empty_view<char>>);
static_assert(CanPipe_R<TestRange, FirstTransformThenMemberRefQualTest&&, ranges::empty_view<short>>);
static_assert(CanPipe_R<TestRange, FirstTransformThenMemberRefQualTest const&, ranges::empty_view<float>>);
static_assert(CanPipe_R<TestRange, FirstTransformThenMemberRefQualTest const&&, ranges::empty_view<double>>);
using FirstMemberRefQualTestThenAll = decltype(RangeAdaptorClosureMemberRefQualTest{} | views::all);
static_assert(CanPipe_R<TestRange, FirstMemberRefQualTestThenAll&, ranges::empty_view<char>>);
static_assert(CanPipe_R<TestRange, FirstMemberRefQualTestThenAll&&, ranges::empty_view<short>>);
static_assert(CanPipe_R<TestRange, FirstMemberRefQualTestThenAll const&, ranges::empty_view<float>>);
static_assert(CanPipe_R<TestRange, FirstMemberRefQualTestThenAll const&&, ranges::empty_view<double>>);
struct RangeAdaptorClosureParameterRefQualTest
: ranges::range_adaptor_closure<RangeAdaptorClosureParameterRefQualTest> {
constexpr char operator()(TestRange&) {
return {};
}
constexpr short operator()(TestRange&&) {
return {};
}
constexpr float operator()(const TestRange&) {
return {};
}
constexpr double operator()(const TestRange&&) {
return {};
}
};
static_assert(CanPipe_R<TestRange&, RangeAdaptorClosureParameterRefQualTest, char>);
static_assert(CanPipe_R<TestRange&&, RangeAdaptorClosureParameterRefQualTest, short>);
static_assert(CanPipe_R<const TestRange&, RangeAdaptorClosureParameterRefQualTest, float>);
static_assert(CanPipe_R<const TestRange&&, RangeAdaptorClosureParameterRefQualTest, double>);
struct MoveOnlyRangeAdaptorClosure : ranges::range_adaptor_closure<MoveOnlyRangeAdaptorClosure> {
MoveOnlyRangeAdaptorClosure() = default;
MoveOnlyRangeAdaptorClosure(const MoveOnlyRangeAdaptorClosure&) = delete;
MoveOnlyRangeAdaptorClosure(MoveOnlyRangeAdaptorClosure&&) = default;
void operator()(const TestRange&) {}
};
static_assert(CanPipe<TestRange, MoveOnlyRangeAdaptorClosure>);
static_assert(CanPipe<TestRange, MoveOnlyRangeAdaptorClosure&>);
static_assert(CanPipe<MoveOnlyRangeAdaptorClosure, IdentityRangeAdaptorClosure>);
static_assert(CanPipe<IdentityRangeAdaptorClosure, MoveOnlyRangeAdaptorClosure>);
static_assert(!CanPipe<MoveOnlyRangeAdaptorClosure&, IdentityRangeAdaptorClosure>);
static_assert(!CanPipe<IdentityRangeAdaptorClosure, MoveOnlyRangeAdaptorClosure&>);
class TimesTwoAdaptor : public ranges::range_adaptor_closure<TimesTwoAdaptor> {
public:
template <ranges::range R>
constexpr auto operator()(R&& range) const {
return forward<R>(range) | views::transform([](auto x) { return x * 2; });
}
};
class DividedByTwoAdaptor : public ranges::range_adaptor_closure<DividedByTwoAdaptor> {
public:
template <ranges::range R>
constexpr auto operator()(R&& range) const {
return forward<R>(range) | views::transform([](auto x) { return x / 2; });
}
};
constexpr bool test_user_defined_adaptors() {
const array<int, 3> numbers{1, 2, 3};
const array<int, 3> numbers_times_two{2, 4, 6};
assert(ranges::equal(numbers_times_two, numbers | TimesTwoAdaptor{}));
assert(ranges::equal(numbers, (numbers | TimesTwoAdaptor{}) | DividedByTwoAdaptor{}));
assert(ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | DividedByTwoAdaptor{})));
assert(ranges::equal(numbers_times_two,
(numbers | TimesTwoAdaptor{})
| (TimesTwoAdaptor{} | TimesTwoAdaptor{} | DividedByTwoAdaptor{} | DividedByTwoAdaptor{})));
return true;
}
constexpr bool test_mixing_of_range_adaptors() {
const array<int, 3> numbers{1, 2, 3};
assert(ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | views::transform([](int x) { return x / 2; }))));
const auto mixed_pipeline = TimesTwoAdaptor{} | views::reverse | DividedByTwoAdaptor{} | views::reverse;
assert(ranges::equal(mixed_pipeline(numbers), numbers));
assert(ranges::equal(numbers | mixed_pipeline, numbers));
auto factory_pipeline = views::iota(1) | TimesTwoAdaptor{} | views::take(3);
assert(ranges::equal(factory_pipeline, array{2, 4, 6}));
return true;
}
int main() {
assert(test_user_defined_adaptors());
static_assert(test_user_defined_adaptors());
assert(test_mixing_of_range_adaptors());
static_assert(test_mixing_of_range_adaptors());
}

Просмотреть файл

@ -272,6 +272,20 @@ STATIC_ASSERT(__cpp_lib_barrier == 201907L);
#endif
#endif
#if _HAS_CXX23
#ifndef __cpp_lib_bind_back
#error __cpp_lib_bind_back is not defined
#elif __cpp_lib_bind_back != 202202L
#error __cpp_lib_bind_back is not 202202L
#else
STATIC_ASSERT(__cpp_lib_bind_back == 202202L);
#endif
#else
#ifdef __cpp_lib_bind_back
#error __cpp_lib_bind_back is defined
#endif
#endif
#if _HAS_CXX20
#ifndef __cpp_lib_bind_front
#error __cpp_lib_bind_front is not defined
@ -1440,7 +1454,15 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L);
STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L);
#endif
#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395
#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395
#ifndef __cpp_lib_ranges
#error __cpp_lib_ranges is not defined
#elif __cpp_lib_ranges != 202202L
#error __cpp_lib_ranges is not 202202L
#else
STATIC_ASSERT(__cpp_lib_ranges == 202202L);
#endif
#elif _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395
#ifndef __cpp_lib_ranges
#error __cpp_lib_ranges is not defined
#elif __cpp_lib_ranges != 202110L