diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2021-04-28 11:40:47 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2021-10-01 20:34:46 +0100 |
commit | d335d73889d897d073b987b4323db05317fccad3 (patch) | |
tree | e0536e7e62fc3341f428a75ae3d09781efdeb7fb | |
parent | 681707ec28d56494fa61a80c62500724d55f8586 (diff) | |
download | gcc-d335d73889d897d073b987b4323db05317fccad3.zip gcc-d335d73889d897d073b987b4323db05317fccad3.tar.gz gcc-d335d73889d897d073b987b4323db05317fccad3.tar.bz2 |
libstdc++: Use conditional noexcept in std::reverse_iterator [PR 94418]
This adds a noexcept-specifier to each constructor and assignment
operator of std::reverse_iterator so that they are noexcept when the
corresponding operation on the underlying iterator is noexcept.
The std::reverse_iterator class template already requires that the
operations on the underlying type are valid, so we don't need to use the
std::is_nothrow_xxx traits to protect against errors when the expression
isn't even valid. We can just use a noexcept operator to test if the
expression can throw, without the overhead of redundantly checking if
the initialization/assignment would be valid.
Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
libstdc++-v3/ChangeLog:
PR libstdc++/94418
* include/bits/stl_iterator.h (reverse_iterator): Use
conditional noexcept on constructors and assignment operators.
* testsuite/24_iterators/reverse_iterator/noexcept.cc: New test.
-rw-r--r-- | libstdc++-v3/include/bits/stl_iterator.h | 20 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/24_iterators/reverse_iterator/noexcept.cc | 92 |
2 files changed, 108 insertions, 4 deletions
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index 004d767..4973f79 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -174,20 +174,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // 235 No specification of default ctor for reverse_iterator // 1012. reverse_iterator default ctor should value initialize _GLIBCXX17_CONSTEXPR - reverse_iterator() : current() { } + reverse_iterator() + _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator())) + : current() + { } /** * This %iterator will move in the opposite direction that @p x does. */ explicit _GLIBCXX17_CONSTEXPR - reverse_iterator(iterator_type __x) : current(__x) { } + reverse_iterator(iterator_type __x) + _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(__x))) + : current(__x) + { } /** * The copy constructor is normal. */ _GLIBCXX17_CONSTEXPR reverse_iterator(const reverse_iterator& __x) - : current(__x.current) { } + _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(__x.current))) + : current(__x.current) + { } #if __cplusplus >= 201103L reverse_iterator& operator=(const reverse_iterator&) = default; @@ -203,7 +211,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif _GLIBCXX17_CONSTEXPR reverse_iterator(const reverse_iterator<_Iter>& __x) - : current(__x.current) { } + _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(__x.current))) + : current(__x.current) + { } #if __cplusplus >= 201103L template<typename _Iter> @@ -214,6 +224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX17_CONSTEXPR reverse_iterator& operator=(const reverse_iterator<_Iter>& __x) + _GLIBCXX_NOEXCEPT_IF(noexcept(current = __x.current)) { current = __x.current; return *this; @@ -226,6 +237,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_NODISCARD _GLIBCXX17_CONSTEXPR iterator_type base() const + _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(current))) { return current; } /** diff --git a/libstdc++-v3/testsuite/24_iterators/reverse_iterator/noexcept.cc b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/noexcept.cc new file mode 100644 index 0000000..df4b1b0 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/noexcept.cc @@ -0,0 +1,92 @@ +// { dg-do compile { target c++11 } } + +#include <iterator> + +template<typename T, bool Nothrow> +struct bidi +{ + using value_type = T; + using pointer = T*; + using reference = T&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; + + T* ptr; + + bidi(T* ptr = nullptr) noexcept(Nothrow) : ptr(ptr) { } + + bidi(const bidi& iter) noexcept(Nothrow) : ptr(iter.ptr) { } + + template<typename U> + bidi(const bidi<U, Nothrow>& iter) noexcept(Nothrow) : ptr(iter.ptr) { } + + bidi& operator=(const bidi& iter) noexcept(Nothrow) + { + ptr = iter.ptr; + return *this; + } + + template<typename U> + bidi& operator=(const bidi<U, Nothrow>& iter) noexcept(Nothrow) + { + ptr = iter.ptr; + return *this; + } + + bidi& operator++() { ++ptr; return *this; } + bidi& operator--() { --ptr; return *this; } + bidi operator++(int) { bidi tmp = *this; ++ptr; return tmp; } + bidi operator--(int) { bidi tmp = *this; --ptr; return tmp; } + + reference operator*() const { return *ptr; } + pointer operator->() const { return ptr; } +}; + +void +test01() +{ + using B1 = bidi<int, true>; + using R1 = std::reverse_iterator<B1>; + static_assert( std::is_nothrow_default_constructible<R1>(), "" ); + static_assert( std::is_nothrow_copy_constructible<R1>(), "" ); + static_assert( std::is_nothrow_move_constructible<R1>(), "" ); + static_assert( std::is_nothrow_copy_assignable<R1>(), "" ); + static_assert( std::is_nothrow_move_assignable<R1>(), "" ); + static_assert( std::is_nothrow_constructible<R1, const B1&>(), "" ); + static_assert( std::is_nothrow_constructible<R1, B1>(), "" ); + + using B2 = bidi<const int, true>; + using R2 = std::reverse_iterator<B2>; + // Test conversions from reverse_iterator<B1> to reverse_iterator<B2>. + static_assert( std::is_nothrow_constructible<R2, const R1&>(), "" ); + static_assert( std::is_nothrow_assignable<R2&, const R1&>(), "" ); + // And from B1 to reverse_iterator<B2>. + static_assert( std::is_nothrow_constructible<R2, const B2&>(), "" ); + static_assert( std::is_nothrow_constructible<R2, B2>(), "" ); + static_assert( std::is_nothrow_constructible<R2, const B1&>(), "" ); + static_assert( std::is_nothrow_constructible<R2, B1>(), "" ); + + using B3 = bidi<int, false>; + using R3 = std::reverse_iterator<B3>; + static_assert( ! std::is_nothrow_default_constructible<R3>(), "" ); + static_assert( ! std::is_nothrow_copy_constructible<R3>(), "" ); + static_assert( ! std::is_nothrow_move_constructible<R3>(), "" ); + static_assert( ! std::is_nothrow_copy_assignable<R3>(), "" ); + static_assert( ! std::is_nothrow_move_assignable<R3>(), "" ); + static_assert( ! std::is_nothrow_constructible<R3, const B3&>(), "" ); + static_assert( ! std::is_nothrow_constructible<R3, B3>(), "" ); + + using B4 = bidi<const int, false>; + using R4 = std::reverse_iterator<B4>; + // Test conversions from reverse_iterator<B3> to reverse_iterator<B4>. + static_assert( ! std::is_nothrow_constructible<R4, const R3&>(), "" ); + static_assert( ! std::is_nothrow_assignable<R4&, const R3&>(), "" ); + // And from B3 to reverse_iterator<B4>. + static_assert( ! std::is_nothrow_constructible<R4, const B4&>(), "" ); + static_assert( ! std::is_nothrow_constructible<R4, B4>(), "" ); + static_assert( ! std::is_nothrow_constructible<R4, const B3&>(), "" ); + static_assert( ! std::is_nothrow_constructible<R4, B3>(), "" ); + + static_assert( noexcept(std::declval<R1&>().base()), "" ); + static_assert( ! noexcept(std::declval<R3&>().base()), "" ); +} |