Skip to content

[Clang] Failed to compile using default three-way comparison in wrapper class when templated type doesn't define comparison #158651

@scott-zhong

Description

@scott-zhong

The following failed to compile on Clang but works with GCC.

#include <memory>

struct UDT
{
};

template <class T>
struct Base {
public:
  T* cbegin() const { return t_; }
  T* cend() const { return t_; }
  friend inline auto operator<=>(const Base& lhs,
                                 const Base& rhs) {
    return std::lexicographical_compare_three_way(
        lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(),
        [](auto&& lhs, auto&& rhs) {
          return std::forward_as_tuple(lhs) <=> std::forward_as_tuple(rhs);
        });
  }
  T* t_;
};

template <class T>
class Wrapper {
public:
  friend inline auto operator<=>(const Wrapper& lhs,
                                 const Wrapper& rhs) = default;

private:
  Base<T> impl_;

};

int main()
{
  Wrapper<UDT> c;
}

The compile and output:

$ clang++ --version && clang++ -std=c++20 -c t.cpp
clang version 16.0.6 (Red Hat 16.0.6-2.module+el8.9.0+19521+190d7aba)
Target: x86_64-redhat-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
t.cpp:17:45: error: invalid operands to binary expression ('tuple<UDT &>' and 'tuple<UDT &>')
          return std::forward_as_tuple(lhs) <=> std::forward_as_tuple(rhs);
                 ~~~~~~~~~~~~~~~~~~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:1826:17: note: in instantiation of function template specialization 'operator<=>(const Base<UDT> &, const Base<UDT> &)::(anonymous class)::operator()<UDT &, UDT &>' requested here
    -> decltype(__comp(*__first1, *__first2))
                ^
t.cpp:14:12: note: while substituting deduced template arguments into function template 'lexicographical_compare_three_way' [with _InputIter1 = UDT *, _InputIter2 = UDT *, _Comp = (lambda at t.cpp:16:9)]
    return std::lexicographical_compare_three_way(
           ^
t.cpp:26:22: note: in instantiation of member function 'operator<=>' requested here
  friend inline auto operator<=>(const Wrapper& lhs,
                     ^
t.cpp:36:16: note: in instantiation of template class 'Wrapper<UDT>' requested here
  Wrapper<UDT> c;
               ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/system_error:316:3: note: candidate function not viable: no known conversion from 'tuple<UDT &>' to 'const error_code' for 1st argument
  operator<=>(const error_code& __lhs, const error_code& __rhs) noexcept
  ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/system_error:498:3: note: candidate function not viable: no known conversion from 'tuple<UDT &>' to 'const error_condition' for 1st argument
  operator<=>(const error_condition& __lhs,
  ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_pair.h:819:5: note: candidate template ignored: could not match 'pair' against 'tuple'
    operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:583:5: note: candidate template ignored: could not match 'reverse_iterator' against 'tuple'
    operator<=>(const reverse_iterator<_IteratorL>& __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:583:5: note: candidate template ignored: could not match 'reverse_iterator' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:601:5: note: candidate template ignored: could not match 'reverse_iterator' against 'tuple'
    operator<=>(const reverse_iterator<_Iterator>& __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:1690:5: note: candidate template ignored: could not match 'move_iterator' against 'tuple'
    operator<=>(const move_iterator<_IteratorL>& __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:1690:5: note: candidate template ignored: could not match 'move_iterator' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_iterator.h:1756:5: note: candidate template ignored: could not match 'move_iterator' against 'tuple'
    operator<=>(const move_iterator<_Iterator>& __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/tuple:1938:5: note: candidate template ignored: substitution failure [with _Tps = <UDT &>, _Ups = <UDT &>]: no matching function for call to object of type 'const struct _Synth3way'
    operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u)
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/tuple:1938:5: note: candidate template ignored: substitution failure [with _Tps = <UDT &>, _Ups = <UDT &>]: no matching function for call to object of type 'const struct _Synth3way'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/string_view:624:5: note: candidate template ignored: could not match 'basic_string_view' against 'tuple'
    operator<=>(basic_string_view<_CharT, _Traits> __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/string_view:632:5: note: candidate template ignored: could not match 'basic_string_view' against 'tuple'
    operator<=>(basic_string_view<_CharT, _Traits> __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/string_view:632:5: note: candidate template ignored: could not match 'basic_string_view' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/basic_string.h:3733:5: note: candidate template ignored: could not match 'basic_string' against 'tuple'
    operator<=>(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/basic_string.h:3748:5: note: candidate template ignored: could not match 'basic_string' against 'tuple'
    operator<=>(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/basic_string.h:3748:5: note: candidate template ignored: could not match 'basic_string' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/unique_ptr.h:988:5: note: candidate template ignored: could not match 'unique_ptr' against 'tuple'
    operator<=>(const unique_ptr<_Tp, _Dp>& __x,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/unique_ptr.h:988:5: note: candidate template ignored: could not match 'unique_ptr' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/unique_ptr.h:997:5: note: candidate template ignored: could not match 'unique_ptr' against 'tuple'
    operator<=>(const unique_ptr<_Tp, _Dp>& __x, nullptr_t)
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/unique_ptr.h:997:5: note: candidate template ignored: could not match 'unique_ptr' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr_base.h:1805:5: note: candidate template ignored: could not match '__shared_ptr' against 'tuple'
    operator<=>(const __shared_ptr<_Tp, _Lp>& __a,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr_base.h:1805:5: note: candidate template ignored: could not match '__shared_ptr' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr_base.h:1811:5: note: candidate template ignored: could not match '__shared_ptr' against 'tuple'
    operator<=>(const __shared_ptr<_Tp, _Lp>& __a, nullptr_t) noexcept
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr_base.h:1811:5: note: candidate template ignored: could not match '__shared_ptr' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr.h:567:5: note: candidate template ignored: could not match 'shared_ptr' against 'tuple'
    operator<=>(const shared_ptr<_Tp>& __a,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr.h:567:5: note: candidate template ignored: could not match 'shared_ptr' against 'tuple'
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr.h:573:5: note: candidate template ignored: could not match 'shared_ptr' against 'tuple'
    operator<=>(const shared_ptr<_Tp>& __a, nullptr_t) noexcept
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/shared_ptr.h:573:5: note: candidate template ignored: could not match 'shared_ptr' against 'tuple'
t.cpp:14:12: error: no matching function for call to 'lexicographical_compare_three_way'
    return std::lexicographical_compare_three_way(
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.cpp:26:22: note: in instantiation of member function 'operator<=>' requested here
  friend inline auto operator<=>(const Wrapper& lhs,
                     ^
t.cpp:36:16: note: in instantiation of template class 'Wrapper<UDT>' requested here
  Wrapper<UDT> c;
               ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:1821:5: note: candidate template ignored: substitution failure [with _InputIter1 = UDT *, _InputIter2 = UDT *, _Comp = (lambda at t.cpp:16:9)]
    lexicographical_compare_three_way(_InputIter1 __first1,
    ^
/opt/rh/gcc-toolset-13/root/usr/lib/gcc/x86_64-redhat-linux/13/../../../../include/c++/13/bits/stl_algobase.h:1869:5: note: candidate function template not viable: requires 4 arguments, but 5 were provided
    lexicographical_compare_three_way(_InputIter1 __first1,
    ^
t.cpp:26:22: error: return type of defaulted 'operator<=>' cannot be deduced because three-way comparison for member 'impl_' has a deduced return type and is not yet defined
  friend inline auto operator<=>(const Wrapper& lhs,
                     ^
t.cpp:36:16: note: in instantiation of template class 'Wrapper<UDT>' requested here
  Wrapper<UDT> c;
               ^
t.cpp:30:11: note: member 'impl_' declared here
  Base<T> impl_;
          ^
t.cpp:12:22: note: selected 'operator<=>' for member 'impl_' declared here
  friend inline auto operator<=>(const Base& lhs,
                     ^
3 errors generated.

Same failure occurs with Clang 19.1.7. The testcase isn't calling any functions that would call the comparison operator, and it shouldn't have failed to compile.

Interestingly, changing the default three-way to the following compiles without any errors.

  friend inline auto operator<=>(const Wrapper& lhs,
                                 const Wrapper& rhs) {
    return lhs.impl_ <=> rhs.impl_;
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"diverges-from:gccDoes the clang frontend diverge from gcc on this issuespaceshipissues related to <=>

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions