[llvm-bugs] [Bug 35235] New: stable_sort() won't compile with function object that takes parameters by non-const reference

via llvm-bugs llvm-bugs at lists.llvm.org
Tue Nov 7 13:27:59 PST 2017


https://bugs.llvm.org/show_bug.cgi?id=35235

            Bug ID: 35235
           Summary: stable_sort() won't compile with function object that
                    takes parameters by non-const reference
           Product: libc++
           Version: 5.0
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P
         Component: All Bugs
          Assignee: unassignedclangbugs at nondot.org
          Reporter: tonyelewis at hotmail.com
                CC: llvm-bugs at lists.llvm.org, mclow.lists at gmail.com

This fails to compile:

~~~
#include <algorithm>
#include <vector>

int main() {
        std::vector<int> a = { 5, 7, 1, 4, 1, 4, 2 };
        std::stable_sort(
                std::begin( a ),
                std::end  ( a ),
                [] (int &x, int &y) { return x < y; }
        );
}
~~~

...but works with std::sort(). The problem is that libstdc++ is trying to call
the function object with a const int (&), which the compiler can't bind a
non-const reference to.

Yet I suspect this code is valid. In point 9 (P896-897) of $25.1 of the C++14
draft at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf, it
says:

> The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied
> to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type
> T when T is part of the signature returns a value testable as true. In other words, if an algorithm takes
> BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should
> work correctly in the construct binary_pred(*first1, *first2) contextually converted to bool (Clause 4).
> BinaryPredicate always takes the first iterator’s value_type as its first argument, that is, in those cases
> when T value is part of the signature, it should work correctly in the construct binary_pred(*first1,
> value) contextually converted to bool (Clause 4). binary_pred shall not apply any non-constant function
> through the dereferenced iterators.

I'm not versed in standardese but I understand the middle part of this as
saying that an implementation of the standard library should work with any
predicate that can take dereferenced iterators as arguments and can be called
in a bool context (so long as it meets other specific requirements).

The errors on GodBolt are:

> In file included from <source>:1:
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:753:62: error: no matching function for call to object of type '(lambda at <source>:9:3)'
>     bool operator()(const _T1& __x, const _T2& __y) {return !__p_(__x, __y);}
>                                                              ^~~~
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4530:13: note: in instantiation of function template specialization 'std::__1::__negate<(lambda at <source>:9:3) &>::operator()<int, int>' requested here
>         if (__comp(*__first2, *__first1))
>             ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4568:9: note: in instantiation of function template specialization 'std::__1::__half_inplace_merge<std::__1::__negate<(lambda at <source>:9:3) &>, std::__1::reverse_iterator<int *>, std::__1::reverse_iterator<std::__1::__wrap_iter<int *> >, std::__1::reverse_iterator<std::__1::__wrap_iter<int *> > >' requested here
>         __half_inplace_merge(_Rv(__p), _Rv(__buff),
>         ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4588:20: note: in instantiation of function template specialization 'std::__1::__buffered_inplace_merge<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>             return __buffered_inplace_merge<_Compare>
>                    ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4869:5: note: in instantiation of function template specialization 'std::__1::__inplace_merge<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __inplace_merge<_Compare>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
>     ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4893:5: note: in instantiation of function template specialization 'std::__1::__stable_sort<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __stable_sort<_Comp_ref>(__first, __last, __comp, __len, __buf.first, __buf.second);
>     ^
> 6 : <source>:6:7: note: in instantiation of function template specialization 'std::__1::stable_sort<std::__1::__wrap_iter<int *>, (lambda at /tmp/compiler-explorer-compiler117107-60-1sp0bjm.260znkx1or/example.cpp:9:3)>' requested here
>         std::stable_sort(
>              ^
> 9 : <source>:9:3: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
>                 [] (int &x, int &y) { return x < y; }
>                 ^
> 9 : <source>:9:3: note: conversion candidate of type 'bool (*)(int &, int &)'
> In file included from <source>:1:
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4332:13: error: no matching function for call to object of type '(lambda at <source>:9:3)'
>         if (__comp(__value_, *__m))
>             ^~~~~~
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4616:20: note: in instantiation of function template specialization 'std::__1::__upper_bound<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *>, int>' requested here
>             __m1 = __upper_bound<_Compare>(__first, __middle, *__m2, __comp);
>                    ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4869:5: note: in instantiation of function template specialization 'std::__1::__inplace_merge<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __inplace_merge<_Compare>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
>     ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4893:5: note: in instantiation of function template specialization 'std::__1::__stable_sort<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __stable_sort<_Comp_ref>(__first, __last, __comp, __len, __buf.first, __buf.second);
>     ^
> 6 : <source>:6:7: note: in instantiation of function template specialization 'std::__1::stable_sort<std::__1::__wrap_iter<int *>, (lambda at /tmp/compiler-explorer-compiler117107-60-1sp0bjm.260znkx1or/example.cpp:9:3)>' requested here
>         std::stable_sort(
>              ^
> 9 : <source>:9:3: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
>                 [] (int &x, int &y) { return x < y; }
>                 ^
> 9 : <source>:9:3: note: conversion candidate of type 'bool (*)(int &, int &)'
> In file included from <source>:1:
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4284:13: error: no matching function for call to object of type '(lambda at <source>:9:3)'
>         if (__comp(*__m, __value_))
>             ^~~~~~
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4631:20: note: in instantiation of function template specialization 'std::__1::__lower_bound<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *>, int>' requested here
>             __m2 = __lower_bound<_Compare>(__middle, __last, *__m1, __comp);
>                    ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4869:5: note: in instantiation of function template specialization 'std::__1::__inplace_merge<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __inplace_merge<_Compare>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
>     ^
> /opt/compiler-explorer/clang-5.0.0/bin/../include/c++/v1/algorithm:4893:5: note: in instantiation of function template specialization 'std::__1::__stable_sort<(lambda at <source>:9:3) &, std::__1::__wrap_iter<int *> >' requested here
>     __stable_sort<_Comp_ref>(__first, __last, __comp, __len, __buf.first, __buf.second);
>     ^
> 6 : <source>:6:7: note: in instantiation of function template specialization 'std::__1::stable_sort<std::__1::__wrap_iter<int *>, (lambda at /tmp/compiler-explorer-compiler117107-60-1sp0bjm.260znkx1or/example.cpp:9:3)>' requested here
>         std::stable_sort(
>              ^
> 9 : <source>:9:3: note: candidate function not viable: 2nd argument ('const int') would lose const qualifier
>                 [] (int &x, int &y) { return x < y; }
>                 ^
> 9 : <source>:9:3: note: conversion candidate of type 'bool (*)(int &, int &)'
> 3 errors generated.
> Compiler exited with result code 1

On a 3.7.0 version of the library, I have been able to get it to compile
cleanly by switching a few bits of code to perfect forward:

> *** stopped.algorithm.20171107	2017-11-07 18:32:58.966543622 +0000
> --- algorithm	2017-11-07 18:38:36.790539013 +0000
> ***************
> *** 734,738 ****
>       template <class _T1, class _T2>
>       _LIBCPP_INLINE_VISIBILITY
> !     bool operator()(const _T1& __x, const _T2& __y) {return !__p_(__x, __y);}
>   };
>   
> --- 734,738 ----
>       template <class _T1, class _T2>
>       _LIBCPP_INLINE_VISIBILITY
> !     bool operator()(_T1&& __x, _T2&& __y) {return !__p_(forward<_T1>(__x), forward<_T2>(__y));}
>   };
>   
> ***************
> *** 4121,4125 ****
>   template <class _Compare, class _ForwardIterator, class _Tp>
>   _ForwardIterator
> ! __lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp)
>   {
>       typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
> --- 4121,4125 ----
>   template <class _Compare, class _ForwardIterator, class _Tp>
>   _ForwardIterator
> ! __lower_bound(_ForwardIterator __first, _ForwardIterator __last, _Tp&& __value_, _Compare __comp)
>   {
>       typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
> ***************
> *** 4130,4134 ****
>           _ForwardIterator __m = __first;
>           _VSTD::advance(__m, __l2);
> !         if (__comp(*__m, __value_))
>           {
>               __first = ++__m;
> --- 4130,4134 ----
>           _ForwardIterator __m = __first;
>           _VSTD::advance(__m, __l2);
> !         if (__comp(*__m, forward<_Tp>(__value_)))
>           {
>               __first = ++__m;
> ***************
> *** 4169,4173 ****
>   template <class _Compare, class _ForwardIterator, class _Tp>
>   _ForwardIterator
> ! __upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp)
>   {
>       typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
> --- 4169,4173 ----
>   template <class _Compare, class _ForwardIterator, class _Tp>
>   _ForwardIterator
> ! __upper_bound(_ForwardIterator __first, _ForwardIterator __last, _Tp&& __value_, _Compare __comp)
>   {
>       typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
> ***************
> *** 4178,4182 ****
>           _ForwardIterator __m = __first;
>           _VSTD::advance(__m, __l2);
> !         if (__comp(__value_, *__m))
>               __len = __l2;
>           else
> --- 4178,4182 ----
>           _ForwardIterator __m = __first;
>           _VSTD::advance(__m, __l2);
> !         if (__comp(forward<_Tp>(__value_), *__m))
>               __len = __l2;
>           else

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20171107/02c7d540/attachment-0001.html>


More information about the llvm-bugs mailing list