libstdc++: potential bug in specification of layouts.

Luc Grosheintz luc.grosheintz@gmail.com
Tue Jun 24 08:59:44 GMT 2025


On 6/23/25 09:43, Tomasz Kaminski wrote:
> 
> I have suggested an alternative of providing a non-member operator ==:
> I think that this could be fixed by changing the friend operator== in
> layout_stride,
> to generic overload in std:
> template<typename StridedMapping1, typename StridedMapping2>
> constexpr bool operator==(const StridedMapping1& lhs, const
> StridedMapping2& rhs);
> Constrains:
>    ** **layout-mapping-alike*
> <https://en.cppreference.com/w/cpp/named_req/LayoutMapping#layout-mapping-alike>
>   <StridedMapping1> && *layout-mapping-alike*
> <https://en.cppreference.com/w/cpp/named_req/LayoutMapping#layout-mapping-alike>
>   <StridedMapping12>
>    * StridedMapping1::extents_type::rank() ==
> StridedMapping2::extents_type::rank()
>    * StridedMapping1::is_always_strided() && StridedMapping2::
> is_always_strided()
> This would allow any strided mapping to be compared without conversions.
> [...]

Thank you, nice suggestion! Though it seems to have the effect of
adding an operator== to certain objects in a way that might be
unintended. I'll explain:

I've implemented the free-standing version:

   template<__mdspan::__mapping_alike _SMapping,
            __mdspan::__mapping_alike _OMapping>
     requires ((_SMapping::extents_type::rank()
                == _OMapping::extents_type::rank())
               && _SMapping::is_always_strided()
               && _OMapping::is_always_strided())
     constexpr bool
     operator==(const _SMapping& __self, const _OMapping& __other)
     noexcept
     {
       // ...
     }

and deleted operator== from all three layouts.

Next, I added a class that only structurally resembles a layout
mapping, but semantically isn't supposed to be a mapping at all,
i.e. it's meant to demonstrate some arbitrary user code that just
happens to look like a mapping.

   template<typename M1, typename M2>
     concept has_op_eq = requires (M1 m1, M2 m2)
     {
       { m1 == m2 } -> std::same_as<bool>;
     };

   // Dummy is only used to play ADL games.
   template<typename Dummy>
     struct not_a_mapping
     {
       using extents_type = std::extents<int, 1>;

       static constexpr bool
       is_always_unique() noexcept { return true; }

       static constexpr bool
       is_always_exhaustive() noexcept { return true; }

       static constexpr bool
       is_always_strided() noexcept { return true; }
     };

   static_assert(std::__mdspan::__mapping_alike<
       not_a_mapping<std::array<int, 2>>>);

   static_assert(has_op_eq<
       not_a_mapping<std::array<int, 2>>,
       not_a_mapping<std::array<int, 2>>>);

My concern is that we've now added a comparison operator to two
user-defined classes. This works due to this part of ADL: the
namespace in which template arguments are defined are also
added to the set of associated namespaces. See, second half
of this paragraph:

https://eel.is/c++draft/basic.lookup.argdep#3.2

Potential fix: we could ask that at least one of the two objects
is a standardized mapping.

Next I'll try to break my suggestion as proposed; but that can be
a separate thread.



More information about the Libstdc++ mailing list