Issue 3031: Algorithms and predicates with non-const reference arguments (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.
3031. Algorithms and predicates with non-const reference arguments
Section: 26.8 [alg.sorting] Status: C++20 Submitter: Jonathan Wakely Opened: 2017-11-08 Last modified: 2021-02-25
Priority: 2
View all other issues in [alg.sorting].
View all issues with C++20 status.
Discussion:
This doesn't compile with any major implementation:
int i[1] = { }; std::stable_sort(i, i, [](int& x, int& y) { return x < y; });
The problem is that the Compare expects non-const references. We say "It is assumed thatcomp will not apply any non-constant function through the dereferenced iterator" But that isn't sufficient to forbid the example.
My first thought was to modify [alg.sorting] to make the Compare requirements usecomp(as_const(x), as_const(x)) but that would get very verbose to add to every expression using comp.
[2017-11 Albuquerque Wednesday night issues processing]
Priority set to 2; Jonathan to improve the statement of the problem.
[2018-02 David Jones provided this truly awful example:]
#include #include #include
struct Base { Base(int value) : v(value) {} friend bool operator<(const Base& l, const Base& r) { return l.v < r.v; } int v; };
struct Derived : public Base { using Base::Base; bool operator<(const Derived& o) /* no const here */ { return v > o.v; } };
int main(void) { std::vector b = {{1}, {5}, {0}, {3}}; std::vector d = {{0}, {1}, {3}, {5}};
std::cout << std::lower_bound(d.begin(), d.end(), 4)->v << std::endl;
std::sort(b.begin(), b.end()); for (const auto &x : b) std::cout << x.v << " "; std::cout << std::endl;
std::sort(d.begin(), d.end()); for (const auto &x : d) std::cout << x.v << " "; std::cout << std::endl; }
libc++:
$ bin/clang++ -std=c++11 -stdlib=libc++ tmp/ex.cc && ./a.out 5 0 1 3 5 0 1 3 5
libstdc++:
$ bin/clang++ -std=c++11 -stdlib=libstdc++ tmp/ex.cc && ./a.out 0 0 1 3 5 5 3 1 0
[2018-08 Batavia Monday issue discussion]
Tim to provide wording; status to 'Open'
[ 2018-08-20, Tim adds P/R based on Batavia discussion.]
Similar to the Ranges TS design, the P/R below requires Predicate,BinaryPredicate, and Compare to accept all mixes ofconst and non-const arguments.
[2018-08-23 Batavia Issues processing]
Status to Tentatively Ready after minor wording nit (corrected in place)
[2018-11, Adopted in San Diego]
Proposed resolution:
This wording is relative to N4762.
- Edit 26.2 [algorithms.requirements] p6-7 as indicated:
-6- The
Predicateparameter is used whenever an algorithm expects a function object (22.10 [function.objects]) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable astrue. In other words, if an algorithm takesPredicate predas its argument andfirstas its iterator argument with value typeT, it should work correctly in the constructpred(*first)contextually converted tobool(7.3 [conv]). The function objectpredshall not apply any non-constant function through the dereferenced iterator. Given a glvalueuof type (possiblyconst)Tthat designates the same object as*first,pred(u)shall be a valid expression that is equal topred(*first).-7- The
BinaryPredicateparameter 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 typeTwhenTis part of the signature returns a value testable astrue. In other words, if an algorithm takesBinaryPredicate binary_predas its argument andfirst1andfirst2as its iterator arguments with respective value typesT1andT2, it should work correctly in the constructbinary_pred(*first1, *first2)contextually converted tobool(7.3 [conv]).Unless otherwise specified,BinaryPredicatealways takes the first iterator'svalue_typeas its first argument, that is, in those cases whenT valueis part of the signature, it should work correctly in the constructbinary_pred(*first1, value)contextually converted tobool(7.3 [conv]).binary_predshall not apply any non-constant function through the dereferenced iterators. Given a glvalueuof type (possiblyconst)T1that designates the same object as*first1, and a glvaluevof type (possiblyconst)T2that designates the same object as*first2,binary_pred(u, *first2),binary_pred(*first1, v), andbinary_pred(u, v)shall each be a valid expression that is equal tobinary_pred(*first1, *first2), andbinary_pred(u, value)shall be a valid expression that is equal tobinary_pred(*first1, value). - Edit 26.8 [alg.sorting] p2 as indicated:
Compareis a function object type (22.10 [function.objects]) that meets the requirements for a template parameter namedBinaryPredicate(26.2 [algorithms.requirements]). The return value of the function call operation applied to an object of typeCompare, when contextually converted tobool(7.3 [conv]), yieldstrueif the first argument of the call is less than the second, andfalseotherwise.Compare compis used throughout for algorithms assuming an ordering relation.It is assumed thatcompwill not apply any non-constant function through the dereferenced iterator.