c++stlset

Count elements lower than a given value in a std::set


I need to find how many elements are lower than a given one in a std::set.

I thought the right function to use was std::lower_bound which returns an iterator to the first element which is greater or equal to the given one....so the index of this iterator is what I'm looking for...but I can't find the index from the iterator:

#include <iostream>
#include <algorithm>
#include <set>

int main()
{
    std::set<int> mySet;
    mySet.insert( 1 );
    mySet.insert( 2 );
    mySet.insert( 3 );
    mySet.insert( 4 );

    std::set<int>::const_iterator found = std::lower_bound( mySet.begin(), mySet.end(), 2 );

    if ( found != mySet.end() )
        std::cout << "Value 2 was found at position " << ( found - mySet.begin() ) << std::endl;
else
        std::cout << "Value 2 was not found" << std::endl;
}

This does not compile:

16:63: error: no match for 'operator-' (operand types are 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}' and 'std::set<int>::iterator {aka std::_Rb_tree_const_iterator<int>}')
16:63: note: candidates are:
In file included from /usr/include/c++/4.9/vector:65:0,
                 from /usr/include/c++/4.9/bits/random.h:34,
                 from /usr/include/c++/4.9/random:49,
                 from /usr/include/c++/4.9/bits/stl_algo.h:66,
                 from /usr/include/c++/4.9/algorithm:62,
                 from 3:

Using std::vector instead of std::set works perfectly.

Looks like operator- is not valid for a std::set::iterator. Why? Then, how can you easily (without calling std::previous or std::next untill bound is reached...this would not be efficient) find the position of a given iterator in the container? If you can't, then what alterantive can I use to find the index of a given element...?


Solution

  • Looks like operator- is not valid for a std::set::iterator. Why?

    Indeed, an implementation of std::set::iterator::operator-() can't exist in constant complexity since the elements are not contiguous in memory.


    Then, how can you easily (without calling std::previous or std::next until bound is reached...this would not be efficient) find the position of a given iterator in the container?

    You can't, std::set::iterator is not a RandomAccessIterator. See std::distance() documentation:

    Complexity

    Linear.


    If you can't, then what alterantive can I use to find the index of a given element...?

    I'd suggest to count your elements without having to compute an iterator distance: std::count_if() can help us:

    #include <iostream>
    #include <algorithm>
    #include <set>
    
    int main()
    {
        std::set<int> mySet;
        mySet.insert( 1 );
        mySet.insert( 2 );
        mySet.insert( 3 );
        mySet.insert( 4 );
    
        const std::size_t lower_than_three = std::count_if(
             std::begin(mySet)
            , std::end(mySet)
            , [](int elem){ return elem < 3; } );
        std::cout << lower_than_three << std::endl;    
    }
    

    Demo