I noticed std::set::equal_range
(same for std::map
) in clang's libc++ gives different result than libstdc++.
I always assumed equal_range
should return equivalent of std::make_pair(set.lower_bound(key), set.upper_bound(key))
which is what cppreference says and libstdc++ does.
In libc++ however I have a code which gives a different result.
#include <set>
#include <iostream>
#include <iterator>
struct comparator {
using range_t = std::pair<int, int>;
using is_transparent = std::true_type;
bool operator()(int lhs, int rhs) const
{
return lhs < rhs;
}
bool operator()(int lhs, range_t rhs) const
{
return lhs < rhs.first;
}
bool operator()(range_t lhs, int rhs) const
{
return lhs.second < rhs;
}
};
using range_set = std::set<int, comparator>;
int main()
{
range_set set = { 1, 3, 6, 10 };
auto range = comparator::range_t{2, 7};
auto eq = set.equal_range(range);
auto low = set.lower_bound(range);
auto high = set.upper_bound(range);
std::cout << "equal_range returned " << std::distance(eq.first, eq.second) << " elem(s): ";
std::copy(eq.first, eq.second, std::ostream_iterator<int>(std::cout, " "));
std::cout << "\nlower/upper returned " << std::distance(low, high) << " elem(s): ";
std::copy(low, high, std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
return 0;
}
As you can see at https://rextester.com/CLTS82056 this gives
equal_range returned 1 elem(s): 3
lower/upper returned 2 elem(s): 3 6
With libstdc++
as well as with boost::container::set
I get 2 elements (which I expect) in both cases.
Of course I can manually call lower_bound
and upper_bound
in my code but using equal_range
is shorter, clearly shows my intention (I want to find all elements which fall into the given range) and might be faster (because equal_range
method could be coded as one tree traversal without actual calls to lower_bound
and upper_bound
).
Interestingly enough, changing container type to std::multiset
in this case makes equal_range
return 2 elements.
Now is this a bug in libc++
or does cppreference give wrong hint in https://en.cppreference.com/w/cpp/container/set/equal_range:
Alternatively, the first iterator may be obtained with
lower_bound()
, and the second withupper_bound()
.
std::set
compare equal to the range value that gets passed in toequal_range
. Given thatstd::set
is supposed to contain unique values only, the libc implementation might be assuming that once it finds a value that compares equal, that's all the set has. I find nothing in a brief review of what N4659 says that supports this. The only thing I see is thatcomparator
should defineis_transparent
. Not sure, but I'll point the finger at libc. – Beem