On Fri, Oct 17, 2025 at 5:27 PM Osama Abdelkader <[email protected]>
wrote:

> Fixes #119721
>
> This fixes a C++23 compliance issue where std::tuple<> cannot be compared
> with std::array<T, 0>. Both are empty tuple-like types and should be
> comparable according to the C++23 standard.
>
> The fix adds the missing comparison operators:
> - operator== and operator!= (return true and false respectively)
> - operator<, operator<=, operator>, operator>= (follow ordering rules for
> empty types)
> - operator<=> (returns strong_ordering::equal)


> This resolves the 'rejects-valid' bug where GCC was rejecting valid C++23
> code
> that Clang with libc++ already supports correctly.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/tuple: Add comparison operators between tuple<> and
> array<T, 0>.
>
> Signed-off-by: Osama Abdelkader <[email protected]>
> ---
>
Thank you for the patch.
As this is C++20 feature, you do not need to define != and <,>,<=,>=.
Just providing ==,<=>. You also do not need to provide them with reversed
order of arguments.

I think you should also consider adding a tuple-like operators to tuple<>
specialization, one that are similar to already present for generic tuple:
   template<__tuple_like _UTuple>
        requires (!__is_tuple_v<_UTuple>)
        friend constexpr bool
        operator== [[nodiscard]] (const tuple& __t, const _UTuple& __u)
        {
          static_assert(tuple_size_v<_UTuple> == 0,
              "tuple objects can only be compared if they have equal
sizes.");
         return true;
        }

      template<__tuple_like _UTuple>
        requires (!__is_tuple_v<_UTuple>)
        strong_ordering
        operator<=>(const tuple& __t, const _UTuple& __u)
        {
            static_assert(tuple_size_v<_UTuple> == 0,
              "tuple objects can only be compared if they have equal
sizes.");
          return std::__tuple_cmp<_Cat>(__t, __u,
index_sequence_for<_Elements...>());
        }
This will also support the span<int, 0> that has apper on the flight.


>  libstdc++-v3/include/std/tuple | 88 ++++++++++++++++++++++++++++++++++
>  1 file changed, 88 insertions(+)
>
> diff --git a/libstdc++-v3/include/std/tuple
> b/libstdc++-v3/include/std/tuple
> index 0ca616f1b..d3621dd16 100644
> --- a/libstdc++-v3/include/std/tuple
> +++ b/libstdc++-v3/include/std/tuple
> @@ -1880,6 +1880,94 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           using _Cat = typename
> __tuple_like_common_comparison_category<_UTuple>::type;
>           return std::__tuple_cmp<_Cat>(__t, __u,
> index_sequence_for<_Elements...>());
>         }
> +
> +  // Comparison operators between tuple<> and array<T, 0>
> +  // These are needed because both are empty tuple-like types in C++23
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator==(const tuple<>&, const array<_Tp, 0>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator==(const array<_Tp, 0>&, const tuple<>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator!=(const tuple<>&, const array<_Tp, 0>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator!=(const array<_Tp, 0>&, const tuple<>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator<(const tuple<>&, const array<_Tp, 0>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator<(const array<_Tp, 0>&, const tuple<>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator<=(const tuple<>&, const array<_Tp, 0>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator<=(const array<_Tp, 0>&, const tuple<>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator>(const tuple<>&, const array<_Tp, 0>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator>(const array<_Tp, 0>&, const tuple<>&)
> +    { return false; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator>=(const tuple<>&, const array<_Tp, 0>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr bool
> +    operator>=(const array<_Tp, 0>&, const tuple<>&)
> +    { return true; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr strong_ordering
> +    operator<=>(const tuple<>&, const array<_Tp, 0>&)
> +    { return strong_ordering::equal; }
> +
> +  template<typename _Tp>
> +    [[nodiscard]]
> +    constexpr strong_ordering
> +    operator<=>(const array<_Tp, 0>&, const tuple<>&)
> +    { return strong_ordering::equal; }
> +
>  #endif // C++23
>
>  #else // ! (concepts && consteval)
> --
> 2.43.0
>
>

Reply via email to