libstdc++: Extend memcmp optimization in std::lexicographical_compare
Jonathan Wakely
jwakely@redhat.com
Tue Jun 9 20:38:28 GMT 2020
On 09/06/20 22:20 +0200, François Dumont via Libstdc++ wrote:
>On 08/06/20 8:20 pm, Jonathan Wakely wrote:
>>On 05/06/20 22:24 +0200, François Dumont via Libstdc++ wrote:
>>>Hi
>>>
>>>ÃÂ ÃÂ ÃÂ Here is the last of my algo patches this time to extend the
>>>memcmp optimization to std::deque iterators and _GLIBCXX_DEBUG
>>>mode.
>>>
>>>ÃÂ ÃÂ ÃÂ To do so I had to return int in implementation details of
>>>lexicographical_compare to make sure we can treat a deque iterator
>>>range by chunk in a performant way.
>>>
>>>Tested under Linux x86_64 normal and debug modes.
>>>
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ * include/bits/stl_algobase.h
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_impl): Return int.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare::__lc): Likewise.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux1(_II1, _II1,
>>>_II2, _II2)): New.
>>>(__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _II2, _II2)): Declare.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux1(_II1, _II1,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _Deque_iterator<>, _Deque_iterator<>)): Declare.
>>>(__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _Deque_iterator<>, _Deque_iterator<>)): Declare.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux): Adapt,
>>>call later.
>>
>>Is this meant to say "latter"? That's still not correct grammar
>>though. I think it would be better to name the function it calls
>>explicitly: "Call __lexicographical_compare_aux1."
>
>Ok.
>
>
>>
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ Ã
>>>(__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _II2, _II2)): Declare.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux(_II1, _II1,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _Safe_iterator<>, _Safe_iterator<>)): Declare.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ Ã
>>>(__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ _Safe_iterator<>, _Safe_iterator<>)): Declare.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (std::lexicographical_compare): Adapt, call
>>>later without __niter_base
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ usage.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ * include/bits/deque.tcc (__lex_cmp_dit): New.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux1): New.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ * include/debug/safe_iterator.tcc
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux(const
>>>_Safe_iterator<>&,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ const _Safe_iterator<>&, _II2, _II2)): New.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ (__lexicographical_compare_aux(
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ const _Safe_iterator<>&, const_Safe_iterator<>&,
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ const _Safe_iterator<>&, const
>>>_Safe_iterator<>&)): New.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ *
>>>testsuite/25_algorithms/lexicographical_compare/1.cc (test6,
>>>test7):
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ New.
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ * testsuite/25_algorithms/lexicographical_compare/deque_iterators/1.cc:
>>>ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ ÃÂ New test.
>>>
>>>Ok to commit ?
>>>
>>>François
>>>
>>>
>>
>>>diff --git a/libstdc++-v3/include/bits/deque.tcc
>>>b/libstdc++-v3/include/bits/deque.tcc
>>>index 1d32a1691c7..d7dbe64f3e1 100644
>>>--- a/libstdc++-v3/include/bits/deque.tcc
>>>+++ b/libstdc++-v3/include/bits/deque.tcc
>>>@@ -1261,6 +1261,98 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â Â Â return true;
>>>Â Â Â }
>>>
>>>+Â template<typename _Tp, typename _Ref, typename _Ptr, typename _II>
>>
>>_II is the wrong name here, it mean InputIterator. All callers of this
>>function are constrained for random access iterators. This cannot be
>>called with an input iterator. Please use _RAIter.
>>
>>>+Â Â Â int
>>>+Â Â Â __lex_cmp_dit(
>>>+Â Â Â const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __first1,
>>>+Â Â Â const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __last1,
>>>+Â Â Â _II __first2, _II __last2)
>>>+Â Â Â {
>>>+Â Â Â Â Â typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> _Iter;
>>>+Â Â Â Â Â typedef typename _Iter::difference_type difference_type;
>>>+
>>>+Â Â Â Â Â if (__first1._M_node != __last1._M_node)
>>>+Â Â Â {
>>>+Â Â Â Â Â difference_type __len = __last2 - __first2;
>>>+Â Â Â Â Â difference_type __flen
>>
>>What does "flen" mean? Why not use len2 for last2 - first2, as we do
>>elsewhere? And then use len for min(len1, len2)?
>
>short for 'first lenght' because it is the first iterator range.
>len1/len2 is better of course.
>
>>
>>
>>>+Â Â Â Â Â Â Â = std::min(__len, __first1._M_last - __first1._M_cur);
>>>+Â Â Â Â Â if (int __ret = std::__lexicographical_compare_aux1(
>>
>>This call (and the three later in this function) will do overload
>>resolution again on the full set of __lexicographical_compare_aux1
>>overloads, which includes the ones for deque iterators. But we know
>>that __first1._M_cur and __first1._M_last are not deque iterators.
>>
>>__first2 could be a deque iterator, but I'm not sure if we really want
>>one function that has to handle that case anyway.
>
>Considering the performance results on the std::copy algo it appeared
>to me that just "unrolling" the deque iterator is already an
>enhancement. Looping on pointers is better than looping on deque
>iterators.
>
>Is it what you wonder ?
Well the question is redundant given my proposed patch, because it
doesn't use __lexicographical_compare_aux1 on the internal pointers. I
added a new function, so that __lexicographical_compare_aux1 doesn't
need to be changed.
>>
>>>+Â Â Â Â Â Â Â Â Â __first1._M_cur, __first1._M_last, __first2, __first2 +
>>>__flen))
>>>+Â Â Â Â Â Â Â return __ret;
>>>+
>>>+Â Â Â Â Â __first2 += __flen;
>>>+Â Â Â Â Â __len -= __flen;
>>>+Â Â Â Â Â __flen = std::min<size_t>(__len, _Iter::_S_buffer_size());
>>>+Â Â Â Â Â for (typename _Iter::_Map_pointer __node = __first1._M_node + 1;
>>>+Â Â Â Â Â Â Â Â Â Â __node != __last1._M_node;
>>>+Â Â Â Â Â Â Â Â Â Â __first2 += __flen, __len -= __flen,
>>>+Â Â Â Â Â Â Â Â Â Â __flen = std::min<size_t>(__len, _Iter::_S_buffer_size()),
>>>+Â Â Â Â Â Â Â Â Â Â ++__node)
>>>+Â Â Â Â Â Â Â if (int __ret = std::__lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â Â Â *__node, *__node + _Iter::_S_buffer_size(),
>>>+Â Â Â Â Â Â Â Â Â __first2, __first2 + __flen))
>>>+Â Â Â Â Â Â Â Â Â return __ret;
>>>+
>>>+Â Â Â Â Â return std::__lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â __last1._M_first, __last1._M_cur, __first2, __last2);
>>>+Â Â Â }
>>>+
>>>+Â Â Â Â Â return std::__lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â Â Â __first1._M_cur, __last1._M_cur, __first2, __last2);
>>>+Â Â Â }
>>>+
>>>+Â template<typename _Tp1, typename _Ref1, typename _Ptr1,
>>>+Â Â Â Â Â Â typename _II2>
>>
>>This is not an input iterator either.
>>
>>>+Â Â Â typename __gnu_cxx::__enable_if<
>>>+Â Â Â Â Â __is_random_access_iter<_II2>::__value, int>::__type
>>
>>This should be inline.
>>
>>Also, I'm curious why these overloads use __enable_if rather than the
>>conventional approach of tag dispatching using iterator_category.
>>(The same question applies to th __equal_aux1 and __copy_move_a1
>>overloads for deque iterators as well). It doesn't really matter
>>though.
>
>Those functions are using Rai operations like operator- so I thought
>that they they should be enabled only on the right category of
>iterator. Maybe overload would have been fine too.
Yes, but tag dispatching also selects the right overload for the right
iterator category.
>Isn't it making overload resolution better ?
No, SFINAE makes overload resolution very expensive.
>>
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
>>>+Â Â Â Â Â Â Â _II2 __first2, _II2 __last2)
>>>+Â Â Â { return std::__lex_cmp_dit(__first1, __last1, __first2,
>>>__last2); }
>>>+
>>>+Â template<typename _Tp1, typename _Ref1, typename _Ptr1,
>>>+Â Â Â Â Â Â typename _Tp2, typename _Ref2, typename _Ptr2>
>>>+Â Â Â int
>>
>>This should be inline.
>>
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
>>>+Â Â Â { return std::__lex_cmp_dit(__first1, __last1, __first2,
>>>__last2); }
>>
>>I'm not convinced it's useful for this function and the one above it
>>to share the same logic.
>This overload is here just to disambiguate a call with 4 deque
>iterators cause in this case the compiler wouldn't know which of the 2
>other overloads to call.
Yes, I understand that part.
>But I still want only 2 implementations, one
>when 1st range are deque iterators and another when 2nd range are
>deque iterators.
But that's silly, the two cases are equivalent, you just need to flip
the result. You don't need two completely different implementations
for comparing deque+other and other+deque. And the two completely
different implementations are *completely* different, for no obvious
reason (one even has a bug that loops forever).
>I just chose that when I have 2 deque iterator ranges
>I'll use the implementation where 1st range is such.
The case where you have two deque iterators can be handled by a
specialized function that unwraps both sets of deque iterators, and
the other two cases can share the same logic.
>>
>>>+Â template<typename _II1,
>>>+Â Â Â Â Â Â typename _Tp2, typename _Ref2, typename _Ptr2>
>>>+Â Â Â typename __gnu_cxx::__enable_if<
>>>+Â Â Â Â Â __is_random_access_iter<_II1>::__value, int>::__type
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _II1 __first1, _II1 __last1,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
>>>+Â Â Â {
>>>+Â Â Â Â Â typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2>
>>>_Iter;
>>>+Â Â Â Â Â typedef typename _Iter::difference_type difference_type;
>>>+
>>>+Â Â Â Â Â difference_type __len = __last1 - __first1;
>>>+Â Â Â Â Â while (__len > 0)
>>>+Â Â Â {
>>>+Â Â Â Â Â const difference_type __flen = __first2._M_node ==
>>>__last2._M_node
>>>+Â Â Â Â Â Â Â ? __last2._M_cur - __first2._M_cur
>>>+Â Â Â Â Â Â Â : __first2._M_last - __first2._M_cur;
>>>+Â Â Â Â Â const difference_type __clen = std::min(__len, __flen);
>>
>>"flen"? "clen"?
>just 'c' for const I think
>>>+Â Â Â Â Â if (int __ret = std::__lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __first1, __first1 + __clen,
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __first2._M_cur, __first2._M_cur + __flen))
>>>+Â Â Â Â Â Â Â return __ret;
>>>+
>>>+Â Â Â Â Â __first1 += __clen;
>>>+Â Â Â Â Â __len -= __clen;
>>>+Â Â Â Â Â __first2 += __clen;
>>>+Â Â Â }
>>>+
>>>+Â Â Â Â Â return __first1 == __last1 ? (__first2 != __last2 ? -1 : 0) : 1;
>>
>>Isn't this function doing basically the same as __lex_cmp_dit? Why
>>does it not use the same logic?
>>
>>Can this whole function just negate the result of __lex_cmp_dit called
>>with the arguments switched?
>>
>>Â return -std::__lex_cmp_dit(__first2, __last2, __first1, __last1);
>>
>>That would also fix the bug that makes the following loop forever:
>>
>>Â std::deque<int> d;
>>Â int i = 0;
>>Â std::lexicographical_compare(&i, &i + 1, d.begin(), d.end());
>I have to consider this, I also wanted to cover comparison with raw
>pointer ranges or vector iterators that could be replaced with memcmp.
Which works fine with the patch I proposed.
>>
>>
>>>+Â Â Â }
>>>+
>>>_GLIBCXX_END_NAMESPACE_VERSION
>>>} // namespace std
>>>
>>>diff --git a/libstdc++-v3/include/bits/stl_algobase.h
>>>b/libstdc++-v3/include/bits/stl_algobase.h
>>>index 0163d8f902d..ecc50a10c9d 100644
>>>--- a/libstdc++-v3/include/bits/stl_algobase.h
>>>+++ b/libstdc++-v3/include/bits/stl_algobase.h
>>>@@ -1279,7 +1279,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>
>>>Â template<typename _II1, typename _II2, typename _Compare>
>>>Â Â Â _GLIBCXX20_CONSTEXPR
>>>-Â Â Â bool
>>>+Â Â Â int
>>>Â Â Â __lexicographical_compare_impl(_II1 __first1, _II1 __last1,
>>>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _II2 __first2, _II2 __last2,
>>>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _Compare __comp)
>>>@@ -1288,16 +1288,18 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â Â Â typedef typename iterator_traits<_II2>::iterator_category
>>>_Category2;
>>>Â Â Â Â Â typedef std::__lc_rai<_Category1, _Category2> __rai_type;
>>>
>>>-Â Â Â Â Â __last1 = __rai_type::__newlast1(__first1, __last1,
>>>__first2, __last2);
>>>-Â Â Â Â Â for (; __first1 != __last1 && __rai_type::__cnd2(__first2,
>>>__last2);
>>>+Â Â Â Â Â _II1 __new_last1
>>>+Â Â Â = __rai_type::__newlast1(__first1, __last1, __first2, __last2);
>>>+Â Â Â Â Â for (; __first1 != __new_last1 &&
>>>__rai_type::__cnd2(__first2, __last2);
>>>Â Â Â Â Â Â ++__first1, (void)++__first2)
>>>Â Â Â Â {
>>>Â Â Â Â Â if (__comp(__first1, __first2))
>>>-Â Â Â Â Â Â Â return true;
>>>+Â Â Â Â Â Â Â return -1;
>>>Â Â Â Â Â if (__comp(__first2, __first1))
>>>-Â Â Â Â Â Â Â return false;
>>>+Â Â Â Â Â Â Â return 1;
>>>Â Â Â Â }
>>>-Â Â Â Â Â return __first1 == __last1 && __first2 != __last2;
>>>+
>>>+Â Â Â Â Â return __first1 == __last1 ? (__first2 != __last2 ? -1 : 0) : 1;
>>
>>This is going to produce worse code for the common (non-deque) case,
>>isn't it?
>>
>>
>>>Â Â Â }
>>>
>>>Â template<bool _BoolType>
>>>@@ -1305,7 +1307,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â {
>>>Â Â Â Â Â template<typename _II1, typename _II2>
>>>Â Â Â Â _GLIBCXX20_CONSTEXPR
>>>-Â Â Â static bool
>>>+Â Â Â static int
>>>Â Â Â Â __lc(_II1 __first1, _II1 __last1, _II2 __first2, _II2 __last2)
>>>Â Â Â Â {
>>>Â Â Â Â Â using __gnu_cxx::__ops::__iter_less_iter;
>>>@@ -1320,7 +1322,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â {
>>>Â Â Â Â Â template<typename _Tp, typename _Up>
>>>Â Â Â Â _GLIBCXX20_CONSTEXPR
>>>-Â Â Â static bool
>>>+Â Â Â static int
>>>Â Â Â Â __lc(const _Tp* __first1, const _Tp* __last1,
>>>Â Â Â Â Â Â Â Â const _Up* __first2, const _Up* __last2)
>>>Â Â Â Â {
>>>@@ -1328,16 +1330,44 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â Â Â const size_t __len2 = __last2 - __first2;
>>>Â Â Â Â Â if (const size_t __len = std::min(__len1, __len2))
>>>Â Â Â Â Â Â Â if (int __result = std::__memcmp(__first1, __first2, __len))
>>>-Â Â Â Â Â Â Â Â Â return __result < 0;
>>>-Â Â Â Â Â return __len1 < __len2;
>>>+Â Â Â Â Â Â Â Â Â return __result;
>>>+
>>>+Â Â Â Â Â return __len1 < __len2 ? -1 : (__len2 < __len1 ? 1 : 0);
>>
>>Again, this will produce worse code.
>>
>>If you made it return ptrdiff_t instead of int you could just do:
>>
>>Â return ptrdiff_t(__len1 - __len2);
>>
>>But I think we should just not change these __lc helpers, and not try
>>to reuse them for deque iterators. We can define new helpers that
>>return the 3-way result that is needed for comparing deque iterators.
>>
>Yes, it sounds promising.
>>
>>>+Â template<typename _Tp1, typename _Ref1, typename _Ptr1,
>>>+Â Â Â Â Â Â typename _II2>
>>>+Â Â Â typename __gnu_cxx::__enable_if<
>>>+Â Â Â Â Â __is_random_access_iter<_II2>::__value, int>::__type
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1>,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1>,
>>>+Â Â Â Â Â Â Â _II2, _II2);
>>>+
>>>+Â template<typename _II1,
>>>+Â Â Â Â Â Â typename _Tp2, typename _Ref2, typename _Ptr2>
>>>+Â Â Â typename __gnu_cxx::__enable_if<
>>>+Â Â Â Â Â __is_random_access_iter<_II1>::__value, int>::__type
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _II1, _II1,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2>,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2>);
>>>+
>>>+Â template<typename _Tp1, typename _Ref1, typename _Ptr1,
>>>+Â Â Â Â Â Â typename _Tp2, typename _Ref2, typename _Ptr2>
>>>+Â Â Â int
>>>+Â Â Â __lexicographical_compare_aux1(
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1>,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1>,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2>,
>>>+Â Â Â Â Â Â Â _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2>);
>>>+
>>>Â template<typename _II1, typename _II2>
>>>Â Â Â _GLIBCXX20_CONSTEXPR
>>>-Â Â Â inline bool
>>>-Â Â Â __lexicographical_compare_aux(_II1 __first1, _II1 __last1,
>>>-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _II2 __first2, _II2 __last2)
>>>+Â Â Â inline int
>>>+Â Â Â __lexicographical_compare_aux1(_II1 __first1, _II1 __last1,
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _II2 __first2, _II2 __last2)
>>
>>If you rename this to __lexicographical_compare_aux2 instead and add
>>a one-line forwarding function called __lexicographical_compare_aux1,
>>then the code above for deque iterators can call the aux2 overload
>>directly, to avoid doing overload resolution on aux1 again.
>>
>>>Â Â Â {
>>>Â Â Â Â Â typedef typename iterator_traits<_II1>::value_type _ValueType1;
>>>Â Â Â Â Â typedef typename iterator_traits<_II2>::value_type _ValueType2;
>>>@@ -1360,6 +1390,43 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
>>>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __first2, __last2);
>>>Â Â Â }
>>>
>>>+Â template<typename _II1, typename _II2>
>>>+Â Â Â _GLIBCXX20_CONSTEXPR
>>>+Â Â Â inline int
>>>+Â Â Â __lexicographical_compare_aux(_II1 __first1, _II1 __last1,
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â _II2 __first2, _II2 __last2)
>>>+Â Â Â {
>>>+Â Â Â Â Â return
>>>std::__lexicographical_compare_aux1(std::__niter_base(__first1),
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__last1),
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__first2),
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__last2));
>>>+Â Â Â }
>>>+
>>>+Â template<typename _Ite1, typename _Seq1, typename _Cat1,
>>
>>Please don't introduce any more "Ite" parameters, this should be _It1
>>or _Iter1.
>>
>>>+Â Â Â Â Â Â typename _II2>
>>>+Â Â Â int
>>>+Â Â Â __lexicographical_compare_aux(
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite1, _Seq1, _Cat1>&,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite1, _Seq1, _Cat1>&,
>>>+Â Â Â Â Â Â Â _II2, _II2);
>>>+
>>>+Â template<typename _II1,
>>>+Â Â Â Â Â Â typename _Ite2, typename _Seq2, typename _Cat2>
>>>+Â Â Â int
>>>+Â Â Â __lexicographical_compare_aux(
>>>+Â Â Â Â Â Â Â _II1, _II1,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite2, _Seq2, _Cat2>&,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite2, _Seq2, _Cat2>&);
>>>+
>>>+Â template<typename _Ite1, typename _Seq1, typename _Cat1,
>>>+Â Â Â Â Â Â typename _Ite2, typename _Seq2, typename _Cat2>
>>>+Â Â Â int
>>>+Â Â Â __lexicographical_compare_aux(
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite1, _Seq1, _Cat1>&,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite1, _Seq1, _Cat1>&,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite2, _Seq2, _Cat2>&,
>>>+Â Â Â Â Â Â Â const ::__gnu_debug::_Safe_iterator<_Ite2, _Seq2, _Cat2>&);
>>>+
>>>Â template<typename _ForwardIterator, typename _Tp, typename _Compare>
>>>Â Â Â _GLIBCXX20_CONSTEXPR
>>>Â Â Â _ForwardIterator
>>>@@ -1659,10 +1726,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
>>>Â Â Â Â Â __glibcxx_requires_valid_range(__first1, __last1);
>>>Â Â Â Â Â __glibcxx_requires_valid_range(__first2, __last2);
>>>
>>>-Â Â Â Â Â return
>>>std::__lexicographical_compare_aux(std::__niter_base(__first1),
>>>-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__last1),
>>>-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__first2),
>>>-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â std::__niter_base(__last2));
>>>+Â Â Â Â Â return std::__lexicographical_compare_aux(__first1, __last1,
>>>+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __first2, __last2) < 0;
>>
>>Why does __lexicographical_compare_aux need to change to return int?
>>
>>It looks like only __lexicographical_compare_aux1 gets called
>>recursively for deque iterators, so only that needs to be able to
>>return -1/0/+1 values.
>>
>Yes, I think I'll follow your idea based on your patch proposal.
I already sent a complete patch.
More information about the Libstdc++
mailing list