You're right, I remember checking this; but it appears I conned
myself. I've reverted the change to <mdspan>, do you want to keep
the tests?

On 7/1/25 12:35, Tomasz Kaminski wrote:
Hi,

I do not think that the change is necessary, as for the functions default
on the first declaration
(as in the case for the constructors/assigments for mdpsan) have noexcept
specifier deduced
from it body. In other words, I think the test should pass, without adding
the complicated noexcept
conditions.

Regards,
Tomasz

On Fri, Jun 27, 2025 at 11:19 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

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