On Mon, Oct 20, 2025 at 05:54:53PM +0200, Tomasz Kaminski wrote:
> On Mon, Oct 20, 2025 at 5:17 PM Osama Abdelkader <[email protected]>
> wrote:
> 
> > This fixes the C++23 compliance issue where std::tuple<> cannot be compared
> > with other empty tuple-like types such as std::array<T, 0>.
> 
> 
> 
> The operators correctly allow comparison with array<T, 0> even when T is not
> > comparable, because empty tuple-like types don't compare element values.
> >
> > libstdc++-v3/ChangeLog:
> >
> >         PR libstdc++/119721
> >
> We usually put the PR reference before the changelog element, and also
> reference
> it in the commit title as [PR119721].
> 
> >         * include/std/tuple: Add tuple<> comparison operators for
> >         empty tuple-like types.
> >
> We usually refer to functions modified in file here,  and if you are adding
> new
> ones a simple "Defined" is sufficient. For example here it would be:
> include/std/tuple (tuple<>::operator==, tuple<>::operator<=>): Define
> See: https://gcc.gnu.org/wiki/ChangeLog
> 
> >         * testsuite/23_containers/tuple/comparison_operators/119721.cc:
> > New test.
> >
> We want to keep a hard column limit here, below 80 lines.
> 
> >
> > Signed-off-by: Osama Abdelkader <[email protected]>
> > ---
> >
> I missed some changes in the test. I have already picked the commit locally,
> so I will post an updated patch.
> 
> > v5:
> > - made the operators hidden friends
> > v4:
> > - Added testsuite test
> > v3:
> > - Added noexcept specifiers to the operators
> > v2:
> > - Replaced explicit array<T, 0> operators with generic tuple-like operators
> > - Only operator== and operator<=> are provided
> > - Operators work with any empty tuple-like type
> > - No need for reversed argument order
> > ---
> >  libstdc++-v3/include/std/tuple                | 19 +++++
> >  .../tuple/comparison_operators/119721.cc      | 71 +++++++++++++++++++
> >  2 files changed, 90 insertions(+)
> >  create mode 100644
> > libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc
> >
> > diff --git a/libstdc++-v3/include/std/tuple
> > b/libstdc++-v3/include/std/tuple
> > index 0ca616f1b..b287694cc 100644
> > --- a/libstdc++-v3/include/std/tuple
> > +++ b/libstdc++-v3/include/std/tuple
> > @@ -1999,6 +1999,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        template<typename _Alloc>
> >         _GLIBCXX20_CONSTEXPR
> >         tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept { }
> > +
> > +#if __cpp_lib_tuple_like // >= C++23
> > +      // Comparison operators for tuple<> with other empty tuple-like
> > types
> > +      // Note: These operators allow comparison with any empty tuple-like
> > type,
> > +      // including array<T, 0>, where T may not be comparable.
> > +      // This is correct because empty tuple-like types don't compare
> > elements.
> > +      template<__tuple_like _UTuple>
> > +       requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0)
> > +      [[nodiscard]]
> > +      friend constexpr bool
> > +      operator==(const tuple&, const _UTuple&) noexcept
> > +      { return true; }
> > +
> > +      template<__tuple_like _UTuple>
> > +       requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0)
> > +      friend constexpr strong_ordering
> > +      operator<=>(const tuple&, const _UTuple&) noexcept
> > +      { return strong_ordering::equal; }
> > +#endif // C++23
> >      };
> >
> >  #if !(__cpp_concepts && __cpp_consteval && __cpp_conditional_explicit) //
> > !C++20
> > diff --git
> > a/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc
> > b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc
> > new file mode 100644
> > index 000000000..711874acf
> > --- /dev/null
> > +++
> > b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc
> > @@ -0,0 +1,71 @@
> > +// { dg-do compile { target c++23 } }
> > +// { dg-options "-std=c++23" }
> > +
> > +// Test for PR libstdc++/119721: tuple<> comparison with array<T, 0>
> > +
> > +#include <tuple>
> > +#include <array>
> > +#include <cassert>
> >
> We use our internal VERIFY macro for test, defined in:
> #include <testsuite_hooks.h>
> 
> 
> > +
> > +void test01()
> > +{
> > +    std::tuple<> t;
> > +    std::array<int, 0> a;
> > +
> > +    // Basic comparison should work
> > +    assert(t == a);
> >
> These become VERIFY macros.
> 
> > +    assert(a == t);
> > +    assert(!(t != a));
> > +    assert(!(a != t));
> > +
> > +    // Ordering comparisons should be equal
> > +    assert(!(t < a));
> > +    assert(!(t > a));
> > +    assert(t <= a);
> > +    assert(t >= a);
> > +    assert(!(a < t));
> > +    assert(!(a > t));
> > +    assert(a <= t);
> > +    assert(a >= t);
> > +
> > +    // Three-way comparison should return equal
> > +    assert((t <=> a) == std::strong_ordering::equal);
> > +    assert((a <=> t) == std::strong_ordering::equal);
> 
> +}
> > +
> > +void test02()
> > +{
> > +    // Test with non-comparable element type
> > +    struct NonComparable {
> > +        void operator==(const NonComparable&) const = delete;
> > +        void operator<=>(const NonComparable&) const = delete;
> > +    };
> > +
> > +    std::tuple<> t;
> > +    std::array<NonComparable, 0> a;
> > +
> > +    // Should still work because empty containers don't compare elements
> > +    assert(t == a);
> > +    assert((t <=> a) == std::strong_ordering::equal);
> > +}
> > +
> > +void test03()
> > +{
> > +    // Test constexpr evaluation
> > +    constexpr std::tuple<> t;
> > +    constexpr std::array<int, 0> a;
> > +
> > +    constexpr bool eq = t == a;
> > +    constexpr auto cmp = t <=> a;
> > +
> > +    static_assert(eq == true);
> > +    static_assert(cmp == std::strong_ordering::equal);
> > +}
> > +
> > +int main()
> > +{
> > +    test01();
> > +    test02();
> > +    test03();
> > +    return 0;
> > +}
> > --
> > 2.43.0
> >
> >

Many thanks Tomasz for doing that yourself in v6 :)
Thank you.

Best regards,
Osama

Reply via email to