Implement views::empty and views::single range factories (#1201)

This commit is contained in:
Casey Carter 2020-08-26 00:25:43 -07:00 коммит произвёл GitHub
Родитель ea1aaf76cb
Коммит 7b0167c84a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 631 добавлений и 8 удалений

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

@ -53,13 +53,6 @@ protected:
_THROW(bad_optional_access{});
}
struct _Nontrivial_dummy_type {
constexpr _Nontrivial_dummy_type() noexcept {
// This default constructor is user-provided to avoid zero-initialization when objects are value-initialized.
}
};
_STL_INTERNAL_STATIC_ASSERT(!is_trivially_default_constructible_v<_Nontrivial_dummy_type>);
template <class _Ty, bool = is_trivially_destructible_v<_Ty>>
struct _Optional_destruct_base { // either contains a value of _Ty or is empty (trivial destructor)
union {

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

@ -12,6 +12,9 @@
#pragma message("The contents of <ranges> are available only with C++20 concepts support.")
#else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv
#include <iterator>
#if 1 // TRANSITION, VSO-1174090
#include <optional>
#endif // TRANSITION, VSO-1174090
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
@ -22,7 +25,7 @@ _STL_DISABLE_CLANG_WARNINGS
_STD_BEGIN
namespace ranges {
// Much machinery defined in <xutility>
// MUCH machinery defined in <xutility>
// clang-format off
// CONCEPT ranges::viewable_range
@ -30,6 +33,401 @@ namespace ranges {
concept viewable_range = range<_Rng>
&& (borrowed_range<_Rng> || view<remove_cvref_t<_Rng>>);
template <class _Ty>
concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>;
// clang-format on
// CLASS TEMPLATE ranges::_Semiregular_box
#if 0 // TRANSITION, VSO-1174090
template <_Copy_constructible_object _Ty>
class _Semiregular_box {
public:
constexpr _Semiregular_box() noexcept : _Dummy{}, _Engaged{false} {}
constexpr _Semiregular_box() noexcept(
is_nothrow_default_constructible_v<_Ty>) requires default_initializable<_Ty>
: _Val(), _Engaged{true} {}
template <class... _Types>
constexpr _Semiregular_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Val(_STD forward<_Types>(_Args)...), _Engaged{true} {}
~_Semiregular_box() requires is_trivially_destructible_v<_Ty> = default;
~_Semiregular_box() {
if (_Engaged) {
_Val.~_Ty();
}
}
_Semiregular_box(const _Semiregular_box&) requires is_trivially_copy_constructible_v<_Ty> = default;
_Semiregular_box(const _Semiregular_box& _That) : _Engaged{_That._Engaged} {
if (_That._Engaged) {
_Construct_in_place(_Val, _That._Val);
}
}
_Semiregular_box(_Semiregular_box&&) requires is_trivially_move_constructible_v<_Ty> = default;
_Semiregular_box(_Semiregular_box&& _That) : _Engaged{_That._Engaged} {
if (_That._Engaged) {
_Construct_in_place(_Val, _STD move(_That._Val));
}
}
// clang-format off
_Semiregular_box& operator=(const _Semiregular_box&) noexcept
requires copyable<_Ty> && is_trivially_copy_assignable_v<_Ty> = default;
// clang-format on
_Semiregular_box& operator=(const _Semiregular_box& _That) noexcept(is_nothrow_copy_constructible_v<_Ty>&&
is_nothrow_copy_assignable_v<_Ty>) /* strengthened */ requires copyable<_Ty> {
if (_Engaged) {
if (_That._Engaged) {
_Val = _That._Val;
} else {
_Val.~_Ty();
_Engaged = false;
}
} else {
if (_That._Engaged) {
_Construct_in_place(_Val, _That._Val);
_Engaged = true;
} else {
// nothing to do
}
}
return *this;
}
_Semiregular_box& operator=(const _Semiregular_box& _That) noexcept(is_nothrow_copy_constructible_v<_Ty>) {
if (_STD addressof(_That) != this) {
if (_Engaged) {
_Val.~_Ty();
_Engaged = false;
}
if (_That._Engaged) {
_Construct_in_place(_Val, _That._Val);
_Engaged = true;
}
}
return *this;
}
// clang-format off
_Semiregular_box& operator=(_Semiregular_box&&) noexcept
requires movable<_Ty> && is_trivially_move_assignable_v<_Ty> = default;
// clang-format on
_Semiregular_box& operator=(_Semiregular_box&& _That) noexcept(is_nothrow_move_constructible_v<_Ty>&&
is_nothrow_move_assignable_v<_Ty>) /* strengthened */ requires movable<_Ty> {
if (_Engaged) {
if (_That._Engaged) {
_Val = _STD move(_That._Val);
} else {
_Val.~_Ty();
_Engaged = false;
}
} else {
if (_That._Engaged) {
_Construct_in_place(_Val, _STD move(_That._Val));
_Engaged = true;
} else {
// nothing to do
}
}
return *this;
}
_Semiregular_box& operator=(_Semiregular_box&& _That) noexcept(is_nothrow_move_constructible_v<_Ty>) {
if (_Engaged) {
_Val.~_Ty();
_Engaged = false;
}
if (_That._Engaged) {
_Construct_in_place(_Val, _STD move(_That._Val));
_Engaged = true;
}
return *this;
}
constexpr explicit operator bool() const noexcept {
return _Engaged;
}
_NODISCARD constexpr _Ty& operator*() noexcept {
_STL_INTERNAL_CHECK(_Engaged);
return _Val;
}
_NODISCARD constexpr const _Ty& operator*() const noexcept {
_STL_INTERNAL_CHECK(_Engaged);
return _Val;
}
private:
union {
_Nontrivial_dummy_type _Dummy;
_Ty _Val;
};
bool _Engaged;
};
// clang-format off
template <_Copy_constructible_object _Ty>
requires default_initializable<_Ty>
&& (copyable<_Ty>
|| (is_nothrow_copy_constructible_v<_Ty>
&& (movable<_Ty> || is_nothrow_move_constructible_v<_Ty>)))
class _Semiregular_box<_Ty> {
// clang-format on
public:
_Semiregular_box() = default;
template <class... _Types>
constexpr _Semiregular_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Val(_STD forward<_Types>(_Args)...) {}
_Semiregular_box(const _Semiregular_box&) = default;
_Semiregular_box(_Semiregular_box&&) = default;
_Semiregular_box& operator=(const _Semiregular_box&) requires copyable<_Ty> = default;
_Semiregular_box& operator=(_Semiregular_box&&) requires movable<_Ty> = default;
_Semiregular_box& operator=(const _Semiregular_box& _That) noexcept {
if (_STD addressof(_That) != this) {
_Val.~_Ty();
_Construct_in_place(_Val, _That._Val);
}
return *this;
}
_Semiregular_box& operator=(_Semiregular_box&& _That) noexcept {
if (_STD addressof(_That) != this) {
_Val.~_Ty();
_Construct_in_place(_Val, _STD move(_That._Val));
}
return *this;
}
constexpr explicit operator bool() const noexcept {
return true;
}
_NODISCARD constexpr _Ty& operator*() noexcept {
return _Val;
}
_NODISCARD constexpr const _Ty& operator*() const noexcept {
return _Val;
}
private:
/* [[no_unique_address]] */ _Ty _Val = _Ty();
};
#else // ^^^ no workaround / workaround vvv
template <class _Ty>
class _Semiregular_box_copy : public optional<_Ty> {
public:
using optional<_Ty>::optional;
_Semiregular_box_copy() = default;
_Semiregular_box_copy(const _Semiregular_box_copy&) = default;
_Semiregular_box_copy(_Semiregular_box_copy&&) = default;
_Semiregular_box_copy& operator=(_Semiregular_box_copy&&) = default;
_Semiregular_box_copy& operator=(const _Semiregular_box_copy& _That) noexcept(
is_nothrow_copy_constructible_v<_Ty>) {
if (_STD addressof(_That) != this) {
optional<_Ty>::reset();
if (_That) {
optional<_Ty>::emplace(*_That);
}
}
return *this;
}
};
template <class _Ty>
using _Choose_semiregular_box_copy =
conditional_t<assignable_from<_Ty&, const _Ty&>, optional<_Ty>, _Semiregular_box_copy<_Ty>>;
template <class _Ty>
class _Semiregular_box_move : public _Choose_semiregular_box_copy<_Ty> {
public:
using _Choose_semiregular_box_copy<_Ty>::_Choose_semiregular_box_copy;
_Semiregular_box_move() = default;
_Semiregular_box_move(const _Semiregular_box_move&) = default;
_Semiregular_box_move(_Semiregular_box_move&&) = default;
_Semiregular_box_move& operator=(const _Semiregular_box_move&) = default;
_Semiregular_box_move& operator=(_Semiregular_box_move&& _That) noexcept(is_nothrow_move_constructible_v<_Ty>) {
if (_STD addressof(_That) != this) {
optional<_Ty>::reset();
if (_That) {
optional<_Ty>::emplace(_STD move(*_That));
}
}
return *this;
}
};
template <class _Ty>
using _Choose_semiregular_box_move =
conditional_t<assignable_from<_Ty&, _Ty>, _Choose_semiregular_box_copy<_Ty>, _Semiregular_box_move<_Ty>>;
template <_Copy_constructible_object _Ty>
class _Semiregular_box : public _Choose_semiregular_box_move<_Ty> {
public:
constexpr _Semiregular_box() noexcept {}
constexpr _Semiregular_box() noexcept(
is_nothrow_default_constructible_v<_Ty>) requires default_initializable<_Ty>
: _Choose_semiregular_box_move<_Ty>{in_place} {}
template <class... _Types>
constexpr _Semiregular_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Choose_semiregular_box_move<_Ty>{in_place, _STD forward<_Types>(_Args)...} {}
};
// clang-format off
template <_Copy_constructible_object _Ty>
requires default_initializable<_Ty> && copyable<_Ty>
class _Semiregular_box<_Ty> {
// clang-format on
public:
_Semiregular_box() = default;
template <class... _Types>
constexpr _Semiregular_box(in_place_t, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
: _Val(_STD forward<_Types>(_Args)...) {}
constexpr explicit operator bool() const noexcept {
return true;
}
_NODISCARD constexpr _Ty& operator*() noexcept {
return _Val;
}
_NODISCARD constexpr const _Ty& operator*() const noexcept {
return _Val;
}
private:
/* [[no_unique_address]] */ _Ty _Val = _Ty();
};
#endif // TRANSITION, VSO-1174090
// CLASS TEMPLATE ranges::empty_view
// clang-format off
template <class _Ty>
requires is_object_v<_Ty>
class empty_view : public view_interface<empty_view<_Ty>> {
// clang-format on
public:
_NODISCARD static constexpr _Ty* begin() noexcept {
return nullptr;
}
_NODISCARD static constexpr _Ty* end() noexcept {
return nullptr;
}
_NODISCARD static constexpr _Ty* data() noexcept {
return nullptr;
}
_NODISCARD static constexpr size_t size() noexcept {
return 0;
}
_NODISCARD static constexpr bool empty() noexcept {
return true;
}
};
namespace views {
// VARIABLE TEMPLATE views::empty
template <class _Ty>
inline constexpr empty_view<_Ty> empty;
} // namespace views
// CLASS TEMPLATE ranges::single_view
// clang-format off
template <copy_constructible _Ty>
requires is_object_v<_Ty>
class single_view : public view_interface<single_view<_Ty>> {
// clang-format on
public:
single_view() = default;
constexpr explicit single_view(const _Ty& _Val_) noexcept(is_nothrow_copy_constructible_v<_Ty>) // strengthened
: _Val{in_place, _Val_} {}
constexpr explicit single_view(_Ty&& _Val_) noexcept(is_nothrow_move_constructible_v<_Ty>) // strengthened
: _Val{in_place, _STD move(_Val_)} {}
// clang-format off
template <class... _Types>
requires constructible_from<_Ty, _Types...>
constexpr explicit single_view(in_place_t, _Types&&... _Args) noexcept( // explicit per LWG-3428
is_nothrow_constructible_v<_Ty, _Types...>) // strengthened
// clang-format on
: _Val{in_place, _STD forward<_Types>(_Args)...} {}
_NODISCARD constexpr _Ty* begin() noexcept {
return data();
}
_NODISCARD constexpr const _Ty* begin() const noexcept {
return data();
}
_NODISCARD constexpr _Ty* end() noexcept {
return data() + 1;
}
_NODISCARD constexpr const _Ty* end() const noexcept {
return data() + 1;
}
_NODISCARD static constexpr size_t size() noexcept {
return 1;
}
_NODISCARD constexpr _Ty* data() noexcept {
return _STD addressof(*_Val);
}
_NODISCARD constexpr const _Ty* data() const noexcept {
return _STD addressof(*_Val);
}
private:
/* [[no_unique_address]] */ _Semiregular_box<_Ty> _Val{};
};
namespace views {
// VARIABLE views::single
struct _Single_fn {
// clang-format off
template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept(
noexcept(single_view{static_cast<_Ty&&>(_Val)})) requires requires {
single_view{static_cast<_Ty&&>(_Val)};
} {
return single_view{static_cast<_Ty&&>(_Val)};
}
// clang-format on
};
inline constexpr _Single_fn single;
} // namespace views
// CLASS TEMPLATE ranges::ref_view
// clang-format off
template <range _Rng>
@ -90,6 +488,8 @@ namespace ranges {
inline constexpr bool enable_borrowed_range<ref_view<_Rng>> = true;
} // namespace ranges
namespace views = ranges::views;
_STD_END
#pragma pop_macro("new")

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

@ -5963,6 +5963,15 @@ template <class _Ty, enable_if_t<is_floating_point_v<_Ty>, int> = 0>
_NODISCARD _CONSTEXPR_BIT_CAST bool _Is_finite(const _Ty _Xx) { // constexpr isfinite()
return _Float_abs_bits(_Xx) < _Float_traits<_Ty>::_Exponent_mask;
}
// STRUCT _Nontrivial_dummy_type
struct _Nontrivial_dummy_type {
constexpr _Nontrivial_dummy_type() noexcept {
// This default constructor is user-provided to avoid zero-initialization when objects are value-initialized.
}
};
_STL_INTERNAL_STATIC_ASSERT(!is_trivially_default_constructible_v<_Nontrivial_dummy_type>);
_STD_END
#undef _CONSTEXPR_BIT_CAST
#pragma pop_macro("new")

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

@ -317,6 +317,8 @@ tests\P0896R4_ranges_ref_view
tests\P0896R4_ranges_subrange
tests\P0896R4_ranges_test_machinery
tests\P0896R4_ranges_to_address
tests\P0896R4_views_empty
tests\P0896R4_views_single
tests\P0898R3_concepts
tests\P0898R3_identity
tests\P0912R5_coroutine

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

@ -124,6 +124,8 @@ STATIC_ASSERT(test_cpo(ranges::empty));
STATIC_ASSERT(test_cpo(ranges::data));
STATIC_ASSERT(test_cpo(ranges::cdata));
STATIC_ASSERT(test_cpo(ranges::views::single));
void test_cpo_ambiguity() {
using namespace std::ranges;

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

@ -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,65 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cassert>
#include <cstddef>
#include <ranges>
#include <string>
using namespace std;
struct not_constructible {
not_constructible() = delete;
~not_constructible() = delete;
};
template <class T>
constexpr bool test_one_type() {
// validate type properties
using R = ranges::empty_view<T>;
static_assert(ranges::view<R> && ranges::contiguous_range<R> && ranges::sized_range<R> && ranges::common_range<R>);
static_assert(same_as<const R, decltype(views::empty<T>)>);
auto& r = views::empty<T>;
// validate member size
static_assert(same_as<decltype(R::size()), size_t>);
static_assert(R::size() == 0);
static_assert(noexcept(R::size()));
static_assert(noexcept(ranges::size(r)));
// validate members begin, data, and end
static_assert(same_as<decltype(R::data()), T*>);
static_assert(R::data() == nullptr);
static_assert(noexcept(R::data()));
static_assert(noexcept(ranges::data(r)));
static_assert(same_as<decltype(R::begin()), T*>);
static_assert(R::begin() == nullptr);
static_assert(noexcept(R::begin()));
static_assert(noexcept(ranges::begin(r)));
static_assert(same_as<decltype(R::end()), T*>);
static_assert(R::end() == nullptr);
static_assert(noexcept(R::end()));
static_assert(noexcept(ranges::end(r)));
// validate member empty
static_assert(same_as<decltype(R::empty()), bool>);
static_assert(R::empty() == true);
static_assert(noexcept(R::empty()));
static_assert(noexcept(ranges::empty(r)));
// validate members inherited from view_interface
assert(!r);
return true;
}
int main() {
static_assert(test_one_type<int>());
test_one_type<int>();
static_assert(test_one_type<string>());
test_one_type<string>();
static_assert(test_one_type<not_constructible>());
test_one_type<not_constructible>();
}

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

@ -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,144 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cassert>
#include <memory>
#include <new>
#include <ranges>
#include <string>
#include <utility>
using namespace std;
template <class>
constexpr bool always_false = false;
struct only_copy_constructible {
int val;
constexpr explicit only_copy_constructible(int i) noexcept : val{i} {}
only_copy_constructible(const only_copy_constructible&) = default;
only_copy_constructible(only_copy_constructible&& that) : val{exchange(that.val, -1)} {}
bool operator==(const only_copy_constructible&) const = default;
template <class T = void>
auto operator&() const {
static_assert(always_false<T>);
}
template <class U>
auto operator,(U&&) const {
static_assert(always_false<U>);
}
};
struct literal_class {
int value;
literal_class() = default;
constexpr explicit literal_class(int i) noexcept : value{i} {}
bool operator==(const literal_class&) const = default;
};
template <class T, class... Args>
constexpr bool test_one_type(T value, Args&&... args) {
// validate type properties
using R = ranges::single_view<T>;
static_assert(ranges::view<R> && ranges::contiguous_range<R> && ranges::sized_range<R> && ranges::common_range<R>);
// validate CTAD and const T& constructor
same_as<R> auto r0 = ranges::single_view{value};
const R& cr0 = r0;
// validate member size
static_assert(same_as<decltype(R::size()), size_t>);
static_assert(R::size() == 1);
static_assert(noexcept(R::size()));
static_assert(noexcept(ranges::size(r0)));
static_assert(noexcept(ranges::size(cr0)));
// validate member data
const same_as<T*> auto ptr = r0.data();
assert(ptr != nullptr);
assert(ptr != addressof(value));
static_assert(noexcept(r0.data()));
static_assert(noexcept(ranges::data(r0)));
const same_as<const T*> auto cptr = cr0.data();
assert(cptr == ptr);
static_assert(noexcept(cr0.data()));
static_assert(noexcept(ranges::data(cr0)));
// validate members begin and end
static_assert(same_as<decltype(r0.begin()), T*>);
assert(*r0.begin() == value);
assert(r0.begin() == ptr);
static_assert(noexcept(r0.begin()));
static_assert(noexcept(ranges::begin(r0)));
static_assert(same_as<decltype(r0.end()), T*>);
assert(r0.end() == ptr + 1);
static_assert(noexcept(r0.end()));
static_assert(noexcept(ranges::end(r0)));
static_assert(same_as<decltype(cr0.begin()), const T*>);
assert(*cr0.begin() == value);
assert(cr0.begin() == cptr);
static_assert(noexcept(cr0.begin()));
static_assert(noexcept(ranges::begin(cr0)));
static_assert(same_as<decltype(cr0.end()), const T*>);
assert(cr0.end() == cptr + 1);
static_assert(noexcept(cr0.end()));
static_assert(noexcept(ranges::end(cr0)));
// validate CTAD and T&& constructor
const same_as<R> auto cr1 = ranges::single_view{move(value)};
assert(cr1.data() != nullptr);
assert(cr1.data() != addressof(value));
assert(cr1.data() != cr0.data());
assert(*cr1.data() == *cr0.data());
// validate in_place constructor
const same_as<R> auto cr2 = ranges::single_view<T>{in_place, forward<Args>(args)...};
assert(cr2.data() != nullptr);
assert(*cr2.data() == *cr1.data());
static_assert(
noexcept(R{in_place, forward<Args>(args)...}) == is_nothrow_constructible_v<T, Args...>); // strengthened
// validate CPO [lvalue]
auto value2 = *cr2.data();
const same_as<R> auto cr3 = views::single(value2);
assert(cr3.data() != nullptr);
assert(cr3.data() != addressof(value2));
assert(*cr3.data() == *cr2.data());
static_assert(noexcept(views::single(value)) == is_nothrow_copy_constructible_v<T>); // strengthened
// validate CPO [rvalue]
const same_as<R> auto cr4 = views::single(move(value2));
assert(cr4.data() != nullptr);
assert(cr4.data() != addressof(value2));
assert(*cr4.data() == *cr3.data());
static_assert(noexcept(views::single(move(value))) == is_nothrow_move_constructible_v<T>); // strengthened
// validate members inherited from view_interface
assert(!cr0.empty());
assert(cr0);
assert(addressof(cr0.front()) == ptr);
assert(addressof(cr0.back()) == ptr);
return true;
}
int main() {
static_assert(test_one_type(42, 42));
test_one_type(42, 42);
static_assert(test_one_type(literal_class{42}, 42));
test_one_type(literal_class{42}, 42);
test_one_type(only_copy_constructible{42}, 42);
test_one_type(string{"Hello, World!"}, "Hello, World!");
}