[Clang] Failed to compile using default three-way comparison in wrapper class when templated type doesn't define comparison
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
The following failed to compile on Clang but works with GCC.
```c++
#include
struct UDT
{
};
template
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 Wrapper {
public:
friend inline auto operator<=>(const Wrapper& lhs,
const Wrapper& rhs) = default;
private:
Base impl_;
};
int main()
{
Wrapper 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' and 'tuple')
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 &, const Base &)::(anonymous class)::operator()' 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' requested here
Wrapper 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' 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' 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 = , _Ups = ]: 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 = , _Ups = ]: 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' requested here
Wrapper 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' requested here
Wrapper c;
^
t.cpp:30:11: note: member 'impl_' declared here
Base 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_;
}
```
Contributor guide
Research direction
Start with the standalone t.cpp testcase from the issue and compile it using clang++ -std=c++20 -c t.cpp, comparing the failure with GCC and the manual comparison definition. Investigate Clang's handling of a defaulted three-way comparison whose member comparison has a deduced return type. Done means the testcase compiles without calling the comparison operator, while preserving correct diagnostics for genuinely invalid comparisons.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100