Efficiently initialise std::set with a sequence of numbers

后端 未结 5 1800
野的像风
野的像风 2020-12-31 01:29

An obvious (naive?) approach would be:

std::set s;
for (int i = 0; i < SIZE; ++i) {
    s.insert(i);
}

That\'s reasonable rea

相关标签:
5条回答
  • 2020-12-31 01:47

    The prettiest would be:

    #include <set>
    #include <boost/iterator/counting_iterator.hpp>
    
    int main()
    {
      const int SIZE = 100;
      std::set<int> s(boost::counting_iterator<int>(0), 
                      boost::counting_iterator<int>(SIZE));
    
      return 0;
    }
    

    If you aim for raw efficiency, using the hinted insert version could be helpful:

    const int SIZE = 100;
    std::set<int> s;
    auto hint = s.begin();
    for(int i = 0; i < SIZE; ++i)
      hint = s.insert(hint, i);
    

    Being able to declaring hint along with the counter would be nice and give us a clean scope, but this requires struct hackery which I find a little obfuscating.

    std::set<int> s;
    for(struct {int i; std::set<int>::iterator hint;} 
          st = {0, s.begin()};
        st.i < SIZE; ++(st.i))
      st.hint = s.insert(st.hint, st.i);
    
    0 讨论(0)
  • 2020-12-31 01:52
    #include <algorithm>
    #include <set>
    #include <iterator>
    
    int main()
    {
        std::set<int> s;
        int i = 0;
        std::generate_n(std::inserter(s, s.begin()), 10, [&i](){ return i++; });
    }
    

    This is (I think) equivalent to your second version, but IMHO looks much better.

    C++03 version would be:

    struct inc {
        static int i;
        explicit inc(int i_) { i = i_; }
        int operator()() { return i++; }
    };
    
    int inc::i = 0;
    
    int main()
    {
        std::set<int> s;
        std::generate_n(std::inserter(s, s.end()), SIZE, inc(0));
    }
    
    0 讨论(0)
  • 2020-12-31 01:54

    The right iterator to use as the hint has changed between C++03 and C++11. With C++03, you want to use the position of the previous item (just as you and most of the replies have shown).

    In C++11, you want to use the iterator to the item immediately after the one you're about to insert. When you're inserting in order, this makes things a bit simpler: you always use your_container.end():

    std::set<int> s;
    for (int i = 0; i < SIZE; ++i) 
        s.insert(s.end(), i);
    

    You can, of course, use an algorithm (e.g., std::iota) or iterator (e.g., boost::counting_iterator, as @pmr already mentioned) to generate your values, but as far as the insertion itself goes, for a current implementation you want to use .end() as the hint, rather than the iterator returned by the previous insertion.

    0 讨论(0)
  • 2020-12-31 02:04

    This can be accomplished in a single line of code. The lambda capture can initialize the variable i to 0 and the mutable specifier allows i to be updated within the lambda function:

    generate_n( inserter( s, s.begin() ), SIZE, [ i=0 ]() mutable { return i++; });
    
    0 讨论(0)
  • 2020-12-31 02:11

    Well you can use the insert() version of set<> in which you can provide the position as hint where the element might get inserted.

    iterator insert ( iterator position, const value_type& x );
    

    Complexity: This version is logarithmic in general, but amortized constant if x is inserted right after the element pointed by position.

    0 讨论(0)
提交回复
热议问题