diff mbox series

[committed] libstdc++: Use conditional noexcept in std::reverse_iterator [PR 94418]

Message ID YVdkNpkOGwZnIQID@redhat.com
State New
Headers show
Series [committed] libstdc++: Use conditional noexcept in std::reverse_iterator [PR 94418] | expand

Commit Message

Jonathan Wakely Oct. 1, 2021, 7:40 p.m. UTC
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.

Tested powerpc64le-linux. Committed to trunk.
commit d335d73889d897d073b987b4323db05317fccad3
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 28 11:40:47 2021

    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.
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 004d767224d..4973f792b56 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 00000000000..df4b1b0763d
--- /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()), "" );
+}