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