mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-28 18:54:55 +00:00
[libc++] Implement the underlying mechanism for range adaptors
This patch implements the underlying mechanism for range adaptors. It does so based on http://wg21.link/p2387, even though that paper hasn't been adopted yet. In the future, if p2387 is adopted, it would suffice to rename `__bind_back` to `std::bind_back` and `__range_adaptor_closure` to `std::range_adaptor_closure` to implement that paper by the spec. Differential Revision: https://reviews.llvm.org/D107098
This commit is contained in:
parent
035325275c
commit
ee44dd8062
@ -227,6 +227,7 @@ set(files
|
||||
__ranges/iota_view.h
|
||||
__ranges/join_view.h
|
||||
__ranges/non_propagating_cache.h
|
||||
__ranges/range_adaptor.h
|
||||
__ranges/ref_view.h
|
||||
__ranges/reverse_view.h
|
||||
__ranges/take_view.h
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__ranges/range_adaptor.h>
|
||||
#include <__ranges/ref_view.h>
|
||||
#include <__ranges/subrange.h>
|
||||
#include <__utility/__decay_copy.h>
|
||||
@ -35,10 +36,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
namespace ranges::views {
|
||||
|
||||
namespace __all {
|
||||
struct __fn {
|
||||
struct __fn : __range_adaptor_closure<__fn> {
|
||||
template<class _Tp>
|
||||
requires ranges::view<decay_t<_Tp>>
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(_VSTD::__decay_copy(_VSTD::forward<_Tp>(__t))))
|
||||
{
|
||||
@ -48,7 +49,7 @@ namespace __all {
|
||||
template<class _Tp>
|
||||
requires (!ranges::view<decay_t<_Tp>>) &&
|
||||
requires (_Tp&& __t) { ranges::ref_view{_VSTD::forward<_Tp>(__t)}; }
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::ref_view{_VSTD::forward<_Tp>(__t)}))
|
||||
{
|
||||
@ -59,7 +60,7 @@ namespace __all {
|
||||
requires (!ranges::view<decay_t<_Tp>> &&
|
||||
!requires (_Tp&& __t) { ranges::ref_view{_VSTD::forward<_Tp>(__t)}; } &&
|
||||
requires (_Tp&& __t) { ranges::subrange{_VSTD::forward<_Tp>(__t)}; })
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::subrange{_VSTD::forward<_Tp>(__t)}))
|
||||
{
|
||||
|
73
libcxx/include/__ranges/range_adaptor.h
Normal file
73
libcxx/include/__ranges/range_adaptor.h
Normal file
@ -0,0 +1,73 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___RANGES_RANGE_ADAPTOR_H
|
||||
#define _LIBCPP___RANGES_RANGE_ADAPTOR_H
|
||||
|
||||
#include <__config>
|
||||
#include <__functional/compose.h>
|
||||
#include <__functional/invoke.h>
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <__utility/move.h>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
// CRTP base that one can derive from in order to be considered a range adaptor closure
|
||||
// by the library. When deriving from this class, a pipe operator will be provided to
|
||||
// make the following hold:
|
||||
// - `x | f` is equivalent to `f(x)`
|
||||
// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))`
|
||||
template <class _Tp>
|
||||
struct __range_adaptor_closure;
|
||||
|
||||
// Type that wraps an arbitrary function object and makes it into a range adaptor closure,
|
||||
// i.e. something that can be called via the `x | f` notation.
|
||||
template <class _Fn>
|
||||
struct __range_adaptor_closure_t : _Fn, __range_adaptor_closure<__range_adaptor_closure_t<_Fn>> {
|
||||
constexpr explicit __range_adaptor_closure_t(_Fn&& __f) : _Fn(_VSTD::move(__f)) { }
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
concept _RangeAdaptorClosure = derived_from<remove_cvref_t<_Tp>, __range_adaptor_closure<remove_cvref_t<_Tp>>>;
|
||||
|
||||
template <class _Tp>
|
||||
struct __range_adaptor_closure {
|
||||
template <ranges::viewable_range _View, _RangeAdaptorClosure _Closure>
|
||||
requires same_as<_Tp, remove_cvref_t<_Closure>> &&
|
||||
invocable<_Closure, _View>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
friend constexpr decltype(auto) operator|(_View&& __view, _Closure&& __closure)
|
||||
noexcept(is_nothrow_invocable_v<_Closure, _View>)
|
||||
{ return _VSTD::invoke(_VSTD::forward<_Closure>(__closure), _VSTD::forward<_View>(__view)); }
|
||||
|
||||
template <_RangeAdaptorClosure _Closure, _RangeAdaptorClosure _OtherClosure>
|
||||
requires same_as<_Tp, remove_cvref_t<_Closure>> &&
|
||||
constructible_from<decay_t<_Closure>, _Closure> &&
|
||||
constructible_from<decay_t<_OtherClosure>, _OtherClosure>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
friend constexpr auto operator|(_Closure&& __c1, _OtherClosure&& __c2)
|
||||
noexcept(is_nothrow_constructible_v<decay_t<_Closure>, _Closure> &&
|
||||
is_nothrow_constructible_v<decay_t<_OtherClosure>, _OtherClosure>)
|
||||
{ return __range_adaptor_closure_t(_VSTD::__compose(_VSTD::forward<_OtherClosure>(__c2), _VSTD::forward<_Closure>(__c1))); }
|
||||
};
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___RANGES_RANGE_ADAPTOR_H
|
@ -10,6 +10,7 @@
|
||||
#define _LIBCPP___RANGES_TRANSFORM_VIEW_H
|
||||
|
||||
#include <__config>
|
||||
#include <__functional/bind_back.h>
|
||||
#include <__functional/invoke.h>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/iter_swap.h>
|
||||
@ -20,8 +21,10 @@
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__ranges/copyable_box.h>
|
||||
#include <__ranges/empty.h>
|
||||
#include <__ranges/range_adaptor.h>
|
||||
#include <__ranges/size.h>
|
||||
#include <__ranges/view_interface.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <__utility/in_place.h>
|
||||
#include <__utility/move.h>
|
||||
#include <concepts>
|
||||
@ -401,6 +404,30 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace views {
|
||||
namespace __transform {
|
||||
struct __fn {
|
||||
template<class _Range, class _Fn>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Range&& __range, _Fn&& __f) const
|
||||
noexcept(noexcept(transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f))))
|
||||
-> decltype( transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f)))
|
||||
{ return transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f)); }
|
||||
|
||||
template<class _Fn>
|
||||
requires constructible_from<decay_t<_Fn>, _Fn>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Fn&& __f) const
|
||||
noexcept(is_nothrow_constructible_v<decay_t<_Fn>, _Fn>)
|
||||
{ return __range_adaptor_closure_t(_VSTD::__bind_back(*this, _VSTD::forward<_Fn>(__f))); }
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto transform = __transform::__fn{};
|
||||
}
|
||||
} // namespace views
|
||||
|
||||
} // namespace ranges
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
@ -647,7 +647,11 @@ module std [system] {
|
||||
|
||||
module __ranges {
|
||||
module access { private header "__ranges/access.h" }
|
||||
module all { private header "__ranges/all.h" }
|
||||
module all {
|
||||
private header "__ranges/all.h"
|
||||
export functional.__functional.compose
|
||||
export functional.__functional.perfect_forward
|
||||
}
|
||||
module common_view { private header "__ranges/common_view.h" }
|
||||
module concepts { private header "__ranges/concepts.h" }
|
||||
module copyable_box { private header "__ranges/copyable_box.h" }
|
||||
@ -662,13 +666,18 @@ module std [system] {
|
||||
module iota_view { private header "__ranges/iota_view.h" }
|
||||
module join_view { private header "__ranges/join_view.h" }
|
||||
module non_propagating_cache { private header "__ranges/non_propagating_cache.h" }
|
||||
module range_adaptor { private header "__ranges/range_adaptor.h" }
|
||||
module ref_view { private header "__ranges/ref_view.h" }
|
||||
module reverse_view { private header "__ranges/reverse_view.h" }
|
||||
module size { private header "__ranges/size.h" }
|
||||
module single_view { private header "__ranges/single_view.h" }
|
||||
module subrange { private header "__ranges/subrange.h" }
|
||||
module take_view { private header "__ranges/take_view.h" }
|
||||
module transform_view { private header "__ranges/transform_view.h" }
|
||||
module transform_view {
|
||||
private header "__ranges/transform_view.h"
|
||||
export functional.__functional.bind_back
|
||||
export functional.__functional.perfect_forward
|
||||
}
|
||||
module view_interface { private header "__ranges/view_interface.h" }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__ranges/range_adaptor.h'}}
|
||||
#include <__ranges/range_adaptor.h>
|
@ -0,0 +1,26 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
// REQUIRES: libc++
|
||||
|
||||
// Test the libc++ extension that std::views::all is marked as [[nodiscard]].
|
||||
|
||||
#include <ranges>
|
||||
|
||||
void test() {
|
||||
int range[] = {1, 2, 3};
|
||||
auto f = [](int i) { return i; };
|
||||
|
||||
std::views::all(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
range | std::views::all; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::transform(f) | std::views::all; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::all | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
@ -15,6 +15,10 @@
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
@ -83,6 +87,11 @@ struct RandomAccessRange {
|
||||
template<>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<RandomAccessRange> = true;
|
||||
|
||||
template <class View, class T>
|
||||
concept CanBePiped = requires (View&& view, T&& t) {
|
||||
{ std::forward<View>(view) | std::forward<T>(t) };
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
ASSERT_SAME_TYPE(decltype(std::views::all(View<true>())), View<true>);
|
||||
@ -142,6 +151,49 @@ constexpr bool test() {
|
||||
assert(std::ranges::end(subrange) == std::ranges::begin(subrange) + 8);
|
||||
}
|
||||
|
||||
// Check SFINAE friendliness of the call operator
|
||||
{
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::all)>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::all), RandomAccessRange, RandomAccessRange>);
|
||||
}
|
||||
|
||||
// Test that std::views::all is a range adaptor
|
||||
{
|
||||
// Test `v | views::all`
|
||||
{
|
||||
Range range(0);
|
||||
auto result = range | std::views::all;
|
||||
ASSERT_SAME_TYPE(decltype(result), std::ranges::ref_view<Range>);
|
||||
assert(&result.base() == &range);
|
||||
}
|
||||
|
||||
// Test `adaptor | views::all`
|
||||
{
|
||||
Range range(0);
|
||||
auto f = [](int i) { return i; };
|
||||
auto const partial = std::views::transform(f) | std::views::all;
|
||||
using Result = std::ranges::transform_view<std::ranges::ref_view<Range>, decltype(f)>;
|
||||
std::same_as<Result> auto result = partial(range);
|
||||
assert(&result.base().base() == &range);
|
||||
}
|
||||
|
||||
// Test `views::all | adaptor`
|
||||
{
|
||||
Range range(0);
|
||||
auto f = [](int i) { return i; };
|
||||
auto const partial = std::views::all | std::views::transform(f);
|
||||
using Result = std::ranges::transform_view<std::ranges::ref_view<Range>, decltype(f)>;
|
||||
std::same_as<Result> auto result = partial(range);
|
||||
assert(&result.base().base() == &range);
|
||||
}
|
||||
|
||||
{
|
||||
struct NotAView { };
|
||||
static_assert( CanBePiped<Range&, decltype(std::views::all)>);
|
||||
static_assert(!CanBePiped<NotAView, decltype(std::views::all)>);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static_assert(std::same_as<decltype(std::views::all), decltype(std::ranges::views::all)>);
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
// REQUIRES: libc++
|
||||
|
||||
// Test the libc++ extension that std::views::transform is marked as [[nodiscard]] to avoid
|
||||
// the potential for user mistakenly thinking they're calling an algorithm.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
void test() {
|
||||
int range[] = {1, 2, 3};
|
||||
auto f = [](int i) { return i; };
|
||||
|
||||
std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::transform(range, f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
range | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::views::transform(f) | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// std::views::transform
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "types.h"
|
||||
|
||||
template <class View, class T>
|
||||
concept CanBePiped = requires (View&& view, T&& t) {
|
||||
{ std::forward<View>(view) | std::forward<T>(t) };
|
||||
};
|
||||
|
||||
struct NonCopyableFunction {
|
||||
NonCopyableFunction(NonCopyableFunction const&) = delete;
|
||||
template <class T>
|
||||
constexpr T operator()(T x) const { return x; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
// Test `views::transform(f)(v)`
|
||||
{
|
||||
{
|
||||
using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
|
||||
std::same_as<Result> auto result = std::views::transform(PlusOne{})(ContiguousView{buff});
|
||||
assert(result.begin().base() == buff);
|
||||
assert(result[0] == 1);
|
||||
assert(result[1] == 2);
|
||||
assert(result[2] == 3);
|
||||
}
|
||||
{
|
||||
auto const partial = std::views::transform(PlusOne{});
|
||||
using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
|
||||
std::same_as<Result> auto result = partial(ContiguousView{buff});
|
||||
assert(result.begin().base() == buff);
|
||||
assert(result[0] == 1);
|
||||
assert(result[1] == 2);
|
||||
assert(result[2] == 3);
|
||||
}
|
||||
}
|
||||
|
||||
// Test `v | views::transform(f)`
|
||||
{
|
||||
{
|
||||
using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
|
||||
std::same_as<Result> auto result = ContiguousView{buff} | std::views::transform(PlusOne{});
|
||||
assert(result.begin().base() == buff);
|
||||
assert(result[0] == 1);
|
||||
assert(result[1] == 2);
|
||||
assert(result[2] == 3);
|
||||
}
|
||||
{
|
||||
auto const partial = std::views::transform(PlusOne{});
|
||||
using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
|
||||
std::same_as<Result> auto result = ContiguousView{buff} | partial;
|
||||
assert(result.begin().base() == buff);
|
||||
assert(result[0] == 1);
|
||||
assert(result[1] == 2);
|
||||
assert(result[2] == 3);
|
||||
}
|
||||
}
|
||||
|
||||
// Test `views::transform(v, f)`
|
||||
{
|
||||
using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
|
||||
std::same_as<Result> auto result = std::views::transform(ContiguousView{buff}, PlusOne{});
|
||||
assert(result.begin().base() == buff);
|
||||
assert(result[0] == 1);
|
||||
assert(result[1] == 2);
|
||||
assert(result[2] == 3);
|
||||
}
|
||||
|
||||
// Test that one can call std::views::transform with arbitrary stuff, as long as we
|
||||
// don't try to actually complete the call by passing it a range.
|
||||
//
|
||||
// That makes no sense and we can't do anything with the result, but it's valid.
|
||||
{
|
||||
struct X { };
|
||||
auto partial = std::views::transform(X{});
|
||||
(void)partial;
|
||||
}
|
||||
|
||||
// Test `adaptor | views::transform(f)`
|
||||
{
|
||||
{
|
||||
using Result = std::ranges::transform_view<std::ranges::transform_view<ContiguousView, PlusOne>, TimesTwo>;
|
||||
std::same_as<Result> auto result = ContiguousView{buff} | std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
|
||||
assert(result.begin().base().base() == buff);
|
||||
assert(result[0] == 2);
|
||||
assert(result[1] == 4);
|
||||
assert(result[2] == 6);
|
||||
}
|
||||
{
|
||||
auto const partial = std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
|
||||
using Result = std::ranges::transform_view<std::ranges::transform_view<ContiguousView, PlusOne>, TimesTwo>;
|
||||
std::same_as<Result> auto result = ContiguousView{buff} | partial;
|
||||
assert(result.begin().base().base() == buff);
|
||||
assert(result[0] == 2);
|
||||
assert(result[1] == 4);
|
||||
assert(result[2] == 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Test SFINAE friendliness
|
||||
{
|
||||
struct NotAView { };
|
||||
struct NotInvocable { };
|
||||
|
||||
static_assert(!CanBePiped<ContiguousView, decltype(std::views::transform)>);
|
||||
static_assert( CanBePiped<ContiguousView, decltype(std::views::transform(PlusOne{}))>);
|
||||
static_assert(!CanBePiped<NotAView, decltype(std::views::transform(PlusOne{}))>);
|
||||
static_assert(!CanBePiped<ContiguousView, decltype(std::views::transform(NotInvocable{}))>);
|
||||
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::transform)>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::transform), PlusOne, ContiguousView>);
|
||||
static_assert( std::is_invocable_v<decltype(std::views::transform), ContiguousView, PlusOne>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::transform), ContiguousView, PlusOne, PlusOne>);
|
||||
static_assert(!std::is_invocable_v<decltype(std::views::transform), NonCopyableFunction>);
|
||||
}
|
||||
|
||||
{
|
||||
static_assert(std::is_same_v<decltype(std::ranges::views::transform), decltype(std::views::transform)>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
@ -129,6 +129,10 @@ struct ThreeWayCompView : std::ranges::view_base {
|
||||
constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); }
|
||||
};
|
||||
|
||||
struct TimesTwo {
|
||||
constexpr int operator()(int x) const { return x * 2; }
|
||||
};
|
||||
|
||||
struct PlusOneMutable {
|
||||
constexpr int operator()(int x) { return x + 1; }
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user