Implement P2494R2 Relaxing Range Adaptors To Allow Move-Only Types (#2965)

Co-authored-by: Nicole Mazzuca <mazzucan@outlook.com>
Co-authored-by: A. Jiang <de34@live.cn>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Co-authored-by: Casey Carter <Casey@Carter.net>
This commit is contained in:
nicole mazzuca 2022-08-26 17:15:58 -07:00 коммит произвёл GitHub
Родитель f241c79ac1
Коммит 04e304b147
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 153 добавлений и 48 удалений

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

@ -47,7 +47,13 @@ namespace ranges {
&& same_as<sentinel_t<_Rng>, sentinel_t<const _Rng>>;
template <class _Ty>
concept _Copy_constructible_object = copy_constructible<_Ty> && _Destructible_object<_Ty>;
concept _Valid_movable_box_object =
#if _HAS_CXX23
move_constructible<_Ty>
#else // ^^^ C++23 / C++20 vvv
copy_constructible<_Ty>
#endif // C++20
&& _Destructible_object<_Ty>;
template <class _It>
concept _Has_arrow = input_iterator<_It>
@ -232,18 +238,21 @@ namespace ranges {
template <bool _Enable, class _Rng, class _Derived>
using _Cached_position_t = conditional_t<_Enable, _Cached_position<_Rng, _Derived>, view_interface<_Derived>>;
template <_Copy_constructible_object _Ty>
class _Copyable_box { // a simplified optional that augments copy_constructible types with full copyability
// A simplified optional that augments copy_constructible types with full copyability,
// and move_constructible types with full movability.
// In C++20, this corresponds to copyable-box.
template <_Valid_movable_box_object _Ty>
class _Movable_box {
public:
constexpr _Copyable_box() noexcept(is_nothrow_default_constructible_v<_Ty>) requires default_initializable<_Ty>
constexpr _Movable_box() noexcept(is_nothrow_default_constructible_v<_Ty>) requires default_initializable<_Ty>
: _Val(), _Engaged{true} {}
template <class... _Types>
constexpr _Copyable_box(in_place_t, _Types&&... _Args) noexcept(
constexpr _Movable_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Val(_STD forward<_Types>(_Args)...), _Engaged{true} {}
constexpr ~_Copyable_box() {
constexpr ~_Movable_box() {
if (_Engaged) {
_Val.~_Ty();
}
@ -251,33 +260,33 @@ namespace ranges {
// TRANSITION, LLVM-46269, destructor order is significant
// clang-format off
~_Copyable_box() requires is_trivially_destructible_v<_Ty> = default;
~_Movable_box() requires is_trivially_destructible_v<_Ty> = default;
_Copyable_box(const _Copyable_box&) requires is_trivially_copy_constructible_v<_Ty> = default;
_Movable_box(const _Movable_box&) requires is_trivially_copy_constructible_v<_Ty> = default;
// clang-format on
constexpr _Copyable_box(const _Copyable_box& _That) : _Engaged{_That._Engaged} {
constexpr _Movable_box(const _Movable_box& _That) : _Engaged{_That._Engaged} {
if (_That._Engaged) {
_Construct_in_place(_Val, _That._Val);
}
}
// clang-format off
_Copyable_box(_Copyable_box&&) requires is_trivially_move_constructible_v<_Ty> = default;
_Movable_box(_Movable_box&&) requires is_trivially_move_constructible_v<_Ty> = default;
// clang-format on
constexpr _Copyable_box(_Copyable_box&& _That) : _Engaged{_That._Engaged} {
constexpr _Movable_box(_Movable_box&& _That) : _Engaged{_That._Engaged} {
if (_That._Engaged) {
_Construct_in_place(_Val, _STD move(_That._Val));
}
}
// clang-format off
_Copyable_box& operator=(const _Copyable_box&) noexcept
_Movable_box& operator=(const _Movable_box&) noexcept
requires copyable<_Ty> && is_trivially_copy_assignable_v<_Ty> = default;
// clang-format on
constexpr _Copyable_box& operator=(const _Copyable_box& _That) noexcept(
constexpr _Movable_box& operator=(const _Movable_box& _That) noexcept(
is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_assignable_v<_Ty>) // strengthened
requires copyable<_Ty> {
if (_Engaged) {
@ -299,7 +308,8 @@ namespace ranges {
return *this;
}
constexpr _Copyable_box& operator=(const _Copyable_box& _That) noexcept(is_nothrow_copy_constructible_v<_Ty>) {
constexpr _Movable_box& operator=(const _Movable_box& _That) noexcept(
is_nothrow_copy_constructible_v<_Ty>) requires copy_constructible<_Ty> {
if (_STD addressof(_That) == this) {
return *this;
}
@ -318,11 +328,11 @@ namespace ranges {
}
// clang-format off
_Copyable_box& operator=(_Copyable_box&&) noexcept
_Movable_box& operator=(_Movable_box&&) noexcept
requires movable<_Ty> && is_trivially_move_assignable_v<_Ty> = default;
// clang-format on
constexpr _Copyable_box& operator=(_Copyable_box&& _That) noexcept(
constexpr _Movable_box& operator=(_Movable_box&& _That) noexcept(
is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_assignable_v<_Ty>) // strengthened
requires movable<_Ty> {
if (_Engaged) {
@ -344,7 +354,7 @@ namespace ranges {
return *this;
}
constexpr _Copyable_box& operator=(_Copyable_box&& _That) noexcept(is_nothrow_move_constructible_v<_Ty>) {
constexpr _Movable_box& operator=(_Movable_box&& _That) noexcept(is_nothrow_move_constructible_v<_Ty>) {
if (_STD addressof(_That) == this) {
return *this;
}
@ -382,32 +392,39 @@ namespace ranges {
bool _Engaged;
};
// clang-format off
template <_Copy_constructible_object _Ty>
requires (copyable<_Ty>
|| (is_nothrow_copy_constructible_v<_Ty>
&& (movable<_Ty> || is_nothrow_move_constructible_v<_Ty>)))
class _Copyable_box<_Ty> { // provide the same API more efficiently when we can avoid the disengaged state
// clang-format on
// [range.move.wrap]
template <class _Ty>
concept _Use_simple_movable_box_wrapper =
(copy_constructible<_Ty>
// 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models
// copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true.
? copyable<_Ty> || (is_nothrow_move_constructible_v<_Ty> && is_nothrow_copy_constructible_v<_Ty>)
// 2. Otherwise, movable-box<T> should store only a T if either T models movable or
// is_nothrow_move_constructible_v<T> is true.
: movable<_Ty> || is_nothrow_move_constructible_v<_Ty>);
template <_Valid_movable_box_object _Ty>
requires _Use_simple_movable_box_wrapper<_Ty>
class _Movable_box<_Ty> { // provide the same API more efficiently when we can avoid the disengaged state
public:
// clang-format off
_Copyable_box() requires default_initializable<_Ty> = default;
_Movable_box() requires default_initializable<_Ty> = default;
// clang-format on
template <class... _Types>
constexpr _Copyable_box(in_place_t, _Types&&... _Args) noexcept(
constexpr _Movable_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Val(_STD forward<_Types>(_Args)...) {}
_Copyable_box(const _Copyable_box&) = default;
_Copyable_box(_Copyable_box&&) = default;
_Movable_box(const _Movable_box&) = default;
_Movable_box(_Movable_box&&) = default;
// clang-format off
_Copyable_box& operator=(const _Copyable_box&) requires copyable<_Ty> = default;
_Copyable_box& operator=(_Copyable_box&&) requires movable<_Ty> = default;
_Movable_box& operator=(const _Movable_box&) requires copyable<_Ty> = default;
_Movable_box& operator=(_Movable_box&&) requires movable<_Ty> = default;
// clang-format on
constexpr _Copyable_box& operator=(const _Copyable_box& _That) noexcept {
constexpr _Movable_box& operator=(const _Movable_box& _That) noexcept requires copy_constructible<_Ty> {
if (_STD addressof(_That) != this) {
_Val.~_Ty();
_Construct_in_place(_Val, _That._Val);
@ -416,7 +433,7 @@ namespace ranges {
return *this;
}
constexpr _Copyable_box& operator=(_Copyable_box&& _That) noexcept {
constexpr _Movable_box& operator=(_Movable_box&& _That) noexcept {
if (_STD addressof(_That) != this) {
_Val.~_Ty();
_Construct_in_place(_Val, _STD move(_That._Val));
@ -883,8 +900,7 @@ namespace ranges {
inline constexpr empty_view<_Ty> empty;
} // namespace views
template <copy_constructible _Ty>
requires is_object_v<_Ty>
template <_Valid_movable_box_object _Ty>
class single_view : public view_interface<single_view<_Ty>> {
public:
// clang-format off
@ -892,7 +908,7 @@ namespace ranges {
// clang-format on
constexpr explicit single_view(const _Ty& _Val_) noexcept(is_nothrow_copy_constructible_v<_Ty>) // strengthened
: _Val{in_place, _Val_} {}
requires copy_constructible<_Ty> : _Val{in_place, _Val_} {}
constexpr explicit single_view(_Ty&& _Val_) noexcept(is_nothrow_move_constructible_v<_Ty>) // strengthened
: _Val{in_place, _STD move(_Val_)} {}
@ -928,7 +944,7 @@ namespace ranges {
}
private:
/* [[no_unique_address]] */ _Copyable_box<_Ty> _Val{};
/* [[no_unique_address]] */ _Movable_box<_Ty> _Val{};
};
template <class _Ty>
@ -1729,7 +1745,7 @@ namespace ranges {
class filter_view : public _Cached_position_t<forward_range<_Vw>, _Vw, filter_view<_Vw, _Pr>> {
private:
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred{};
/* [[no_unique_address]] */ _Movable_box<_Pr> _Pred{};
template <class _View>
struct _Category_base {};
@ -1987,15 +2003,15 @@ namespace ranges {
#define _NOEXCEPT_IDL0(...)
#endif // _ITERATOR_DEBUG_LEVEL == 0
template <input_range _Vw, copy_constructible _Fn>
requires view<_Vw> && is_object_v<_Fn>
template <input_range _Vw, _Valid_movable_box_object _Fn>
requires view<_Vw>
&& regular_invocable<_Fn&, range_reference_t<_Vw>>
&& _Can_reference<invoke_result_t<_Fn&, range_reference_t<_Vw>>>
class transform_view : public view_interface<transform_view<_Vw, _Fn>> {
// clang-format on
private:
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ _Copyable_box<_Fn> _Fun{};
/* [[no_unique_address]] */ _Movable_box<_Fn> _Fun{};
template <bool _Const>
class _Sentinel;
@ -2692,7 +2708,7 @@ namespace ranges {
class take_while_view : public view_interface<take_while_view<_Vw, _Pr>> {
private:
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred{};
/* [[no_unique_address]] */ _Movable_box<_Pr> _Pred{};
template <bool _Const, bool _Wrapped = true>
class _Sentinel {
@ -3064,7 +3080,7 @@ namespace ranges {
class drop_while_view : public _Cached_position_t<forward_range<_Vw>, _Vw, drop_while_view<_Vw, _Pr>> {
private:
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred{};
/* [[no_unique_address]] */ _Movable_box<_Pr> _Pred{};
public:
// clang-format off
@ -6289,7 +6305,7 @@ namespace ranges {
class chunk_by_view : public _Cached_position<_Vw, chunk_by_view<_Vw, _Pr>> {
private:
/* [[no_unique_address]] */ _Vw _Range{};
/* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred{};
/* [[no_unique_address]] */ _Movable_box<_Pr> _Pred{};
class _Iterator {
private:

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

@ -332,6 +332,7 @@
// P2443R1 views::chunk_by
// P2445R1 forward_like()
// P2446R2 views::as_rvalue
// P2494R2 Relaxing Range Adaptors To Allow Move-Only Types
// P2499R0 string_view Range Constructor Should Be explicit
// P2549R0 unexpected<E>::error()
@ -1570,9 +1571,9 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#if defined(__cpp_lib_concepts) // TRANSITION, GH-395
#if _HAS_CXX23
#define __cpp_lib_ranges 202202L // P2387R3 Pipe Support For User-Defined Range Adaptors
#define __cpp_lib_ranges 202207L // P2494R2 Relaxing Range Adaptors To Allow Move-Only Types
#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv
#define __cpp_lib_ranges 202110L // P2415R2 What Is A `view`?
#define __cpp_lib_ranges 202110L // P2415R2 What Is A view?
#endif // _HAS_CXX20
#endif // defined(__cpp_lib_concepts)

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

@ -561,6 +561,7 @@ tests\P2443R1_views_chunk_by
tests\P2443R1_views_chunk_by_death
tests\P2445R1_forward_like
tests\P2446R2_views_as_rvalue
tests\P2494R2_move_only_range_adaptors
tests\P2517R1_apply_conditional_noexcept
tests\VSO_0000000_allocator_propagation
tests\VSO_0000000_any_calling_conventions

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

@ -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,83 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <array>
#include <cassert>
#include <iterator>
#include <ranges>
#include <type_traits>
#include <utility>
#include <vector>
using namespace std;
struct BaseTransform {
explicit BaseTransform(int v) : val(v) {}
int operator()(int x) const {
return val * x;
}
int val;
};
enum SmfKind {
Nothrow,
Throwing,
Deleted,
};
template <SmfKind CCtor, SmfKind MCtor, SmfKind CAssign, SmfKind MAssign>
struct Transform : BaseTransform {
using BaseTransform::BaseTransform;
Transform(const Transform&) noexcept(CCtor == Nothrow) //
requires(CCtor != Deleted) = default;
Transform(const Transform&) requires(CCtor == Deleted) = delete;
Transform(Transform&&) noexcept(MCtor == Nothrow) //
requires(MCtor != Deleted) = default;
Transform(Transform&&) requires(MCtor == Deleted) = delete;
Transform& operator=(const Transform&) noexcept(CAssign == Nothrow) //
requires(CAssign != Deleted) = default;
Transform& operator=(const Transform&) requires(CAssign == Deleted) = delete;
Transform& operator=(Transform&&) noexcept(MAssign == Nothrow) //
requires(MAssign != Deleted) = default;
Transform& operator=(Transform&&) requires(MAssign == Deleted) = delete;
};
template <SmfKind CCtor, SmfKind MCtor, SmfKind CAssign, SmfKind MAssign>
void test_transform() {
using T = Transform<CCtor, MCtor, CAssign, MAssign>;
T t{11};
vector<int> v;
ranges::copy(array{0, 1, 2, 3, 4, 5} | views::transform(move(t)), back_inserter(v));
assert(ranges::equal(v, array{0, 11, 22, 33, 44, 55}));
if constexpr (is_copy_constructible_v<T>
? copyable<T> || (is_nothrow_copy_constructible_v<T> && is_nothrow_move_constructible_v<T>)
: movable<T> || is_nothrow_move_constructible_v<T>) {
static_assert(sizeof(ranges::_Movable_box<T>) == sizeof(T));
} else {
static_assert(sizeof(ranges::_Movable_box<T>) > sizeof(T));
}
}
int main() {
test_transform<Nothrow, Nothrow, Nothrow, Nothrow>();
test_transform<Throwing, Throwing, Throwing, Throwing>();
test_transform<Nothrow, Nothrow, Deleted, Deleted>();
test_transform<Nothrow, Throwing, Deleted, Deleted>();
test_transform<Throwing, Nothrow, Deleted, Deleted>();
test_transform<Throwing, Throwing, Deleted, Deleted>();
test_transform<Deleted, Nothrow, Deleted, Nothrow>();
test_transform<Deleted, Nothrow, Deleted, Throwing>();
test_transform<Deleted, Nothrow, Deleted, Deleted>();
test_transform<Deleted, Throwing, Deleted, Nothrow>();
test_transform<Deleted, Throwing, Deleted, Throwing>();
test_transform<Deleted, Throwing, Deleted, Deleted>();
}

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

@ -1457,10 +1457,10 @@ STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L);
#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
#elif __cpp_lib_ranges != 202207L
#error __cpp_lib_ranges is not 202207L
#else
STATIC_ASSERT(__cpp_lib_ranges == 202202L);
STATIC_ASSERT(__cpp_lib_ranges == 202207L);
#endif
#elif _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395
#ifndef __cpp_lib_ranges