I need to process a list of objects of type Foo
in groups sharing the quality of corresponding to the same value of Bar
. The list is pre-sorted in relation to that quality, so my idea was to use std::upper_bound
to find where the subsequent groups begin.
Bar FooToBar(const Foo &foo);
// sorted so that FooToBar(foolist[0] <= FooToBar(foolist[1]) <= ...
std::list<Foo> foolist;
// find bounds of a group of Foo-s corresponding to someBar;
Bar someBar;
auto
groupBegin = foolist.begin(),
// find last item of foolist whose FooToBar() == someBar
groupEnd = std::upper_bound( foolist.begin(),
foolist.end(),
someBar );
Of course that will not work because Foo
and Bar
are not directly comparable. Luckily, there's an overload of std::upper_bound
which takes an extra comparator argument:
groupEnd = std::upper_bound( foolist.begin(), foolist.end(), someBar, Compare);
Question is, how do I go about writing Compare()
? Here's where things get interesting. cppreference.com says:
The signature of the comparison function should be equivalent to the following:
bool cmp(const Type1 &a, const Type2 &b);
The signature does not need to have const &, but the function object must not modify the objects passed to it. The types Type1 and Type2 must be such that an object of type T can be implicitly converted to both Type1 and Type2, and an object of type ForwardIt can be dereferenced and then implicitly converted to both Type1 and Type2.
Obviously, there's no way I can satisfy those conditions with Foo
and Bar
. However, cplusplus.com says something different:
Binary function that accepts two arguments (the first is always val, and the second of the type pointed by ForwardIterator), and returns a value convertible to bool.
I can work with that, so:
bool Compare(const Bar &bar, const Foo &foo) { /* ... */ }
However, that does not compile in either VS2013, nor g++:
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/include/c++/bits/predefined_ops.h:141:37: error: cannot convert ‘Foo’ to ‘Bar’ in argument passing
Curiously, when I reverse the argument order, it compiles, runs and behaves as expected:
bool Compare(const Foo &foo, const Bar &bar) { /* ... */ }
So it looks like one reference says one thing, other reference says something else, and the compiler accepts something still different. Or did I misunderstand something?
What you're refering to is a defect in the standard: #270. The original wording was deemed to strict (indeed, your particular use case was mentioned). The section in the Standard now reads, [upper.bound]:
template<class ForwardIterator, class T> ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& value); template<class ForwardIterator, class T, class Compare> ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
Requires: The elements
e
of[first,last)
shall be partitioned with respect to the expression!(value < e)
or!comp(value, e)
.
Returns: The furthermost iteratori
in the range[first,last]
such that for every iteratorj
in the range[first,i)
the following corresponding conditions hold:!(value < *j)
orcomp(value, *j) == false
.
In both cases, value
is the first argument to comp
and the element is second. So the following is perfectly valid code:
struct Foo { };
struct Bar { };
std::vector<Foo> foolist;
auto it = std::upper_bound(foolist.begin(), foolist.end(), Bar{},
[](Bar const&, Foo const&) { return false; });
The above works on both gcc 5.2 (and even 4.6.4 -- modulo the lambda -- which is the oldest I have easy access to) and clang 3.6.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With