I'm not sure what you mean, but it does weird to me that the underlying
types can't be compared, but the corresponding tuple-like can be compared:

#include <tuple>
#include <array>

struct S {
void operator== (const S&) const = delete;
void operator<=>(const S&) const = delete;
void operator <(const S&) const = delete;
};

int main() {
std::tuple<> t;
std::array<S, 0> a;
// a == a; // error
t < a; // ok
t == a; // ok
t <=> a; // ok
}

Tomasz Kaminski <[email protected]> 於 2025年10月18日 週六 上午12:20寫道:

>
>
> On Fri, Oct 17, 2025 at 6:00 PM Hewill Kang <[email protected]> wrote:
>
>> Wow, I just noticed that the current wording allows tuple<> to be
>> compared to any array<T, 0>, regardless of the type of T.
>> However, tuple<T> can only be compared to array<T, 1> if T itself is
>> comparable.
>>
> They common_reference is tuple<>, but they are not totally_ordered_with,
> as array is not ordered.
>
>> This seems to be a defect.
>>
>> Osama Abdelkader <[email protected]> 於 2025年10月17日 週五 下午11:27寫道:
>>
>>> 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]>
>>> ---
>>>  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