libstdc++: Add noexcept specifiers to some range adaptors

Signed-off-by: Jonathan Wakely <jwakely@redhat.com>

libstdc++-v3/ChangeLog:

	* include/bits/ranges_util.h (view_interface): Add noexcept to
	empty, operator bool, data and size members.
	(subrange): Add noexcept to constructors.
	* include/std/ranges (single_view, ref_view): Add noexcept to
	constructors.
	(views::single, views::all): Add noexcept.
	* testsuite/std/ranges/adaptors/all.cc: Check noexcept.
	* testsuite/std/ranges/single_view.cc: Likewise.
This commit is contained in:
Jonathan Wakely 2021-06-15 16:36:12 +01:00
parent a88fc03ba7
commit 9245b0e84c
4 changed files with 119 additions and 16 deletions

View file

@ -77,45 +77,67 @@ namespace ranges
return static_cast<const _Derived&>(*this);
}
static constexpr bool
_S_bool(bool) noexcept; // not defined
template<typename _Tp>
static constexpr bool
_S_empty(_Tp& __t)
noexcept(noexcept(_S_bool(ranges::begin(__t) == ranges::end(__t))))
{ return ranges::begin(__t) == ranges::end(__t); }
template<typename _Tp>
static constexpr auto
_S_size(_Tp& __t)
noexcept(noexcept(ranges::end(__t) - ranges::begin(__t)))
{ return ranges::end(__t) - ranges::begin(__t); }
public:
constexpr bool
empty() requires forward_range<_Derived>
{ return ranges::begin(_M_derived()) == ranges::end(_M_derived()); }
empty()
noexcept(noexcept(_S_empty(_M_derived())))
requires forward_range<_Derived>
{ return _S_empty(_M_derived()); }
constexpr bool
empty() const requires forward_range<const _Derived>
{ return ranges::begin(_M_derived()) == ranges::end(_M_derived()); }
empty() const
noexcept(noexcept(_S_empty(_M_derived())))
requires forward_range<const _Derived>
{ return _S_empty(_M_derived()); }
constexpr explicit
operator bool() requires requires { ranges::empty(_M_derived()); }
operator bool() noexcept(noexcept(ranges::empty(_M_derived())))
requires requires { ranges::empty(_M_derived()); }
{ return !ranges::empty(_M_derived()); }
constexpr explicit
operator bool() const requires requires { ranges::empty(_M_derived()); }
operator bool() const noexcept(noexcept(ranges::empty(_M_derived())))
requires requires { ranges::empty(_M_derived()); }
{ return !ranges::empty(_M_derived()); }
constexpr auto
data() requires contiguous_iterator<iterator_t<_Derived>>
{ return to_address(ranges::begin(_M_derived())); }
data() noexcept(noexcept(ranges::begin(_M_derived())))
requires contiguous_iterator<iterator_t<_Derived>>
{ return std::to_address(ranges::begin(_M_derived())); }
constexpr auto
data() const
data() const noexcept(noexcept(ranges::begin(_M_derived())))
requires range<const _Derived>
&& contiguous_iterator<iterator_t<const _Derived>>
{ return to_address(ranges::begin(_M_derived())); }
{ return std::to_address(ranges::begin(_M_derived())); }
constexpr auto
size()
size() noexcept(noexcept(_S_size(_M_derived())))
requires forward_range<_Derived>
&& sized_sentinel_for<sentinel_t<_Derived>, iterator_t<_Derived>>
{ return ranges::end(_M_derived()) - ranges::begin(_M_derived()); }
{ return _S_size(_M_derived()); }
constexpr auto
size() const
size() const noexcept(noexcept(_S_size(_M_derived())))
requires forward_range<const _Derived>
&& sized_sentinel_for<sentinel_t<const _Derived>,
iterator_t<const _Derived>>
{ return ranges::end(_M_derived()) - ranges::begin(_M_derived()); }
{ return _S_size(_M_derived()); }
constexpr decltype(auto)
front() requires forward_range<_Derived>
@ -223,6 +245,8 @@ namespace ranges
constexpr
subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s)
noexcept(is_nothrow_constructible_v<_It, decltype(__i)>
&& is_nothrow_constructible_v<_Sent, _Sent&>)
requires (!_S_store_size)
: _M_begin(std::move(__i)), _M_end(__s)
{ }
@ -230,6 +254,8 @@ namespace ranges
constexpr
subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s,
__size_type __n)
noexcept(is_nothrow_constructible_v<_It, decltype(__i)>
&& is_nothrow_constructible_v<_Sent, _Sent&>)
requires (_Kind == subrange_kind::sized)
: _M_begin(std::move(__i)), _M_end(__s)
{
@ -242,7 +268,9 @@ namespace ranges
&& __detail::__convertible_to_non_slicing<iterator_t<_Rng>, _It>
&& convertible_to<sentinel_t<_Rng>, _Sent>
constexpr
subrange(_Rng&& __r) requires _S_store_size && sized_range<_Rng>
subrange(_Rng&& __r)
noexcept(noexcept(subrange(__r, ranges::size(__r))))
requires _S_store_size && sized_range<_Rng>
: subrange(__r, ranges::size(__r))
{ }
@ -251,7 +279,9 @@ namespace ranges
&& __detail::__convertible_to_non_slicing<iterator_t<_Rng>, _It>
&& convertible_to<sentinel_t<_Rng>, _Sent>
constexpr
subrange(_Rng&& __r) requires (!_S_store_size)
subrange(_Rng&& __r)
noexcept(noexcept(subrange(ranges::begin(__r), ranges::end(__r))))
requires (!_S_store_size)
: subrange(ranges::begin(__r), ranges::end(__r))
{ }
@ -260,6 +290,7 @@ namespace ranges
&& convertible_to<sentinel_t<_Rng>, _Sent>
constexpr
subrange(_Rng&& __r, __size_type __n)
noexcept(noexcept(subrange(ranges::begin(__r), ranges::end(__r), __n)))
requires (_Kind == subrange_kind::sized)
: subrange{ranges::begin(__r), ranges::end(__r), __n}
{ }

View file

@ -197,11 +197,13 @@ namespace ranges
constexpr explicit
single_view(const _Tp& __t)
noexcept(is_nothrow_copy_constructible_v<_Tp>)
: _M_value(__t)
{ }
constexpr explicit
single_view(_Tp&& __t)
noexcept(is_nothrow_move_constructible_v<_Tp>)
: _M_value(std::move(__t))
{ }
@ -211,6 +213,7 @@ namespace ranges
requires constructible_from<_Tp, _Args...>
constexpr explicit
single_view(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
: _M_value{in_place, std::forward<_Args>(__args)...}
{ }
@ -604,6 +607,7 @@ namespace views
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
noexcept(noexcept(single_view<decay_t<_Tp>>(std::forward<_Tp>(__e))))
{ return single_view<decay_t<_Tp>>(std::forward<_Tp>(__e)); }
};
@ -1022,6 +1026,7 @@ namespace views::__adaptor
&& requires { _S_fun(declval<_Tp>()); }
constexpr
ref_view(_Tp&& __t)
noexcept(noexcept(static_cast<_Range&>(std::declval<_Tp>())))
: _M_r(std::__addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
{ }
@ -1069,12 +1074,25 @@ namespace views::__adaptor
struct _All : __adaptor::_RangeAdaptorClosure
{
template<typename _Range>
static constexpr bool
_S_noexcept()
{
if constexpr (view<decay_t<_Range>>)
return is_nothrow_constructible_v<decay_t<_Range>, _Range>;
else if constexpr (__detail::__can_ref_view<_Range>)
return true;
else
return noexcept(subrange{std::declval<_Range>()});
}
template<viewable_range _Range>
requires view<decay_t<_Range>>
|| __detail::__can_ref_view<_Range>
|| __detail::__can_subrange<_Range>
constexpr auto
operator()(_Range&& __r) const
noexcept(_S_noexcept<_Range>())
{
if constexpr (view<decay_t<_Range>>)
return std::forward<_Range>(__r);

View file

@ -130,6 +130,35 @@ test05()
static_assert(!requires { 0 | all; });
}
template<bool B1, bool B2>
struct BorrowedRange
{
int* ptr = nullptr;
BorrowedRange(int (&arr)[3]) noexcept : ptr(arr) { }
int* begin() const noexcept(B1) { return ptr; }
int* end() const noexcept(B2) { return ptr + 3; }
};
template<bool B1, bool B2>
const bool std::ranges::enable_borrowed_range<BorrowedRange<B1, B2>> = true;
void
test06()
{
int x[] { 1, 2, 3 };
// Using ref_view:
static_assert(noexcept(views::all(x)));
// Using subrange:
static_assert(noexcept(views::all(BorrowedRange<true, true>(x))));
static_assert(!noexcept(views::all(BorrowedRange<true, false>(x))));
static_assert(!noexcept(views::all(BorrowedRange<false, true>(x))));
static_assert(!noexcept(views::all(BorrowedRange<false, false>(x))));
}
int
main()
{
@ -138,4 +167,5 @@ main()
static_assert(test03());
static_assert(test04());
test05();
test06();
}

View file

@ -73,10 +73,34 @@ test04()
std::as_const(s).data();
}
void
test05()
{
int i = 0;
static_assert(noexcept(std::ranges::single_view<int>()));
static_assert(noexcept(std::ranges::single_view<int>(i)));
static_assert(noexcept(std::ranges::single_view<int>(1)));
static_assert(noexcept(std::ranges::single_view<int>(std::in_place, 2)));
static_assert(noexcept(std::ranges::views::single(i)));
auto s = std::ranges::views::single(i);
static_assert(noexcept(s.begin()));
static_assert(noexcept(s.end()));
static_assert(noexcept(s.size()));
static_assert(noexcept(s.data()));
static_assert(noexcept(s.empty())); // view_interface::empty()
const auto cs = s;
static_assert(noexcept(cs.begin()));
static_assert(noexcept(cs.end()));
static_assert(noexcept(cs.size()));
static_assert(noexcept(cs.data()));
static_assert(noexcept(cs.empty())); // view_interface::empty()
}
int main()
{
test01();
test02();
test03();
test04();
test05();
}