If all members of mdspan are nothrow movable, then mdspan can also
be nothrow movable. The standard doesn't specify that mdspan must be
nothrow movable (when possible). Nothrow movable enables containers
to use move operations even if they have a strong exception guarantee.

This commit strenghtens the exception guarantees for mdspan, making
it nothrow movable if all it's members are nothrow movable.

libstdc++-v3/ChangeLog:

        * include/std/mdspan (mdspan): Make nothrow movable, if all
        members are nothrow movable.
        * testsuite/23_containers/mdspan/mdspan.cc: Test nothrow movable
        property.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
 libstdc++-v3/include/std/mdspan               | 10 +++-
 .../testsuite/23_containers/mdspan/mdspan.cc  | 53 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index 852f881971e..563aa312f8a 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -1119,7 +1119,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       mdspan(const mdspan& __other) = default;
 
       constexpr
-      mdspan(mdspan&& __other) = default;
+      mdspan(mdspan&& __other)
+      noexcept(is_nothrow_move_constructible_v<accessor_type>
+         && is_nothrow_move_constructible_v<mapping_type>
+         && is_nothrow_move_constructible_v<data_handle_type>) = default;
 
       template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
        requires ((sizeof...(_OIndexTypes) == rank()
@@ -1197,7 +1200,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       operator=(const mdspan& __other) = default;
 
       constexpr mdspan&
-      operator=(mdspan&& __other) = default;
+      operator=(mdspan&& __other)
+      noexcept(is_nothrow_move_assignable_v<accessor_type>
+         && is_nothrow_move_assignable_v<mapping_type>
+         && is_nothrow_move_assignable_v<data_handle_type>) = default;
 
       template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
        requires (sizeof...(_OIndexTypes) == rank())
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
index d2672878d69..6d8f32ff103 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
@@ -310,11 +310,29 @@ test_from_int_like()
   verify(std::mdspan(storage.data(), shape_view));
 }
 
-template<typename T>
+template<typename T, bool NothrowConstructible = true,
+        bool NothrowAssignable = true>
   class OpaqueAccessor
   {
     struct Handle
     {
+      constexpr
+      Handle(T * other)
+      : ptr(other)
+      { }
+
+      constexpr
+      Handle(const Handle&) noexcept(NothrowConstructible) = default;
+
+      constexpr
+      Handle(Handle&&) noexcept(NothrowConstructible) = default;
+
+      constexpr Handle&
+      operator=(const Handle&) noexcept(NothrowAssignable) = default;
+
+      constexpr Handle&
+      operator=(Handle&&) noexcept(NothrowAssignable) = default;
+
       T * ptr;
     };
 
@@ -489,6 +507,37 @@ test_swap()
   return true;
 }
 
+template<bool Constructible, bool Assignable>
+constexpr void
+test_nothrow_movable()
+{
+  using Layout = std::layout_left;
+  using Extents = std::dextents<int, 3>;
+  using Accessor = OpaqueAccessor<int, Constructible, Assignable>;
+  using Handle = Accessor::data_handle_type;
+  static_assert(std::is_nothrow_move_assignable_v<Accessor>);
+  static_assert(std::is_nothrow_move_constructible_v<Accessor>);
+  static_assert(std::is_nothrow_move_assignable_v<Handle> == Assignable);
+  static_assert(std::is_nothrow_move_constructible_v<Handle> == Constructible);
+
+  using MDSpan = std::mdspan<int, Extents, Layout, Accessor>;
+  static_assert(std::is_nothrow_move_assignable_v<MDSpan> == Assignable);
+  static_assert(std::is_nothrow_move_constructible_v<MDSpan> == Constructible);
+}
+
+constexpr void
+test_nothrow_movable_all()
+{
+  using MDSpan = std::mdspan<double, std::dextents<int, 3>>;
+  static_assert(std::is_nothrow_move_assignable_v<MDSpan>);
+  static_assert(std::is_nothrow_move_constructible_v<MDSpan>);
+
+  test_nothrow_movable<true, true>();
+  test_nothrow_movable<true, false>();
+  test_nothrow_movable<false, true>();
+  test_nothrow_movable<false, false>();
+}
+
 int
 main()
 {
@@ -536,5 +585,7 @@ main()
 
   test_swap();
   static_assert(test_swap());
+
+  test_nothrow_movable_all();
   return 0;
 }
-- 
2.49.0

Reply via email to