Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

libc++'s implementation of std::map/set::equal_range gives unexpected results

Tags:

c++

libc++

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 with upper_bound().

like image 389
koval Avatar asked Aug 18 '19 14:08

koval


1 Answers

Your code is fine.

You are testing on an outdated libc++. This is bug 30959, fixed in 2018 and available in clang 7.

Rextester's clang is apparently 3.8.

like image 69
T.C. Avatar answered Nov 02 '22 00:11

T.C.