The following code compiles before C++20:
#include <algorithm>
#include <vector>
struct Foo
{
};
struct FooArray
{
std::vector<Foo> a;
};
bool operator<(const FooArray& a, const FooArray& b)
{
return std::lexicographical_compare(std::begin(a.a), std::end(a.a), std::begin(b.a), std::end(b.a));
}
bool operator<(const Foo& a, const Foo& b)
{
return false;
}
With c++20 enabled GCC 10 and 11 reject the code, but only when optimisations are enabled:
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h: In instantiation of 'constexpr bool __gnu_cxx::__ops::_Iter_less_iter::operator()(_Iterator1, _Iterator2) const [with _Iterator1 = const Foo*; _Iterator2 = const Foo*]':
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1240:14: required from 'constexpr bool std::__lexicographical_compare_impl(_II1, _II1, _II2, _II2, _Compare) [with _II1 = const Foo*; _II2 = const Foo*; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1257:46: required from 'static constexpr bool std::__lexicographical_compare<_BoolType>::__lc(_II1, _II1, _II2, _II2) [with _II1 = const Foo*; _II2 = const Foo*; bool _BoolType = false]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1302:60: required from 'constexpr bool std::__lexicographical_compare_aux(_II1, _II1, _II2, _II2) [with _II1 = const Foo*; _II2 = const Foo*]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1605:48: required from 'constexpr bool std::lexicographical_compare(_II1, _II1, _II2, _II2) [with _II1 = __gnu_cxx::__normal_iterator<const Foo*, std::vector<Foo> >; _II2 = __gnu_cxx::__normal_iterator<const Foo*, std::vector<Foo> >]'
<source>:15:40: required from here
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: error: no match for 'operator<' (operand types are 'const Foo' and 'const Foo')
43 | { return *__it1 < *__it2; }
| ~~~~~~~^~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:67,
from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1097:5: note: candidate: 'template<class _IteratorL, class _IteratorR, class _Container> constexpr std::__detail::__synth3way_t<_IteratorR, _IteratorL> __gnu_cxx::operator<=>(const __gnu_cxx::__normal_iterator<_IteratorL, _Container>&, const __gnu_cxx::__normal_iterator<_IteratorR, _Container>&)' (reversed)
1097 | operator<=>(const __normal_iterator<_IteratorL, _Container>& __lhs,
| ^~~~~~~~
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1097:5: note: template argument deduction/substitution failed:
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: note: 'const Foo' is not derived from 'const __gnu_cxx::__normal_iterator<_IteratorL, _Container>'
43 | { return *__it1 < *__it2; }
| ~~~~~~~^~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:67,
from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1114:5: note: candidate: 'template<class _Iterator, class _Container> constexpr std::__detail::__synth3way_t<_T1> __gnu_cxx::operator<=>(const __gnu_cxx::__normal_iterator<_Iterator, _Container>&, const __gnu_cxx::__normal_iterator<_Iterator, _Container>&)' (rewritten)
1114 | operator<=>(const __normal_iterator<_Iterator, _Container>& __lhs,
| ^~~~~~~~
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1114:5: note: template argument deduction/substitution failed:
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: note: 'const Foo' is not derived from 'const __gnu_cxx::__normal_iterator<_Iterator, _Container>'
43 | { return *__it1 < *__it2; }
| ~~~~~~~^~~~~~~~
<source>:13:6: note: candidate: 'bool operator<(const FooArray&, const FooArray&)'
13 | bool operator<(const FooArray& a, const FooArray& b)
| ^~~~~~~~
<source>:13:32: note: no known conversion for argument 1 from 'const Foo' to 'const FooArray&'
13 | bool operator<(const FooArray& a, const FooArray& b)
| ~~~~~~~~~~~~~~~~^
Compiler returned: 1
GCC 12 and later compile the code. MSVC always compiles the code, clang rejects it when c++20 is enabled.
https://godbolt.org/z/9ra6hn7c9
Which compiler is correct? Does bool operator<(const Foo& a, const Foo& b)
need to be declared before calling std::lexicographical_compare
? Is this a change in c++20 or did the code just happen to work in c++17 (I was surprised the code wasn't causing problems in c++17 too)?
Best Answer
This is ill-formed no diagnostic required as per temp.param#7.
First note that there are two point of instantiations(POI). The first POI is at namespace scope just after
operator<(const FooArray& a, const FooArray& b)
as per temp.point:The second POI is at the end of the translation unit.
Now as per temp.point#7:
This means that the given program is IFNDR because it violates basic.def.odr:
The above is violated because at the first POI, there is no
operator<
that we can refer to but at the second POI there is. So they don't refer to the same function.The solution is to provide a declaration before first POI so that the referred to functions are the samea at both the POIs.