Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot use std::iota with std::set

I'd like to create a set of a range of numbers: 0, 1, 2, 3, 4, ... The following code fails to compile:

std::set<int> s;
std::iota(s.begin(), s.end(), 0);

with the following error message:

error C3892: '_First' : you cannot assign to a variable that is const

The compiler is VC++2012. The same code works fine for a vector. How should I use it with a set?

UPDATE

I can see now that my code is meaningless, because there's no set size specified.

Here are some more details about my problem.

I have a set containing SOME numbers from [0, N] range. In my application I need to calculate set difference many times for such sets. N is fixed.

Let's say N = 5 and the first set is s1 = {0, 3, 4}. I need to calculate set difference {0, 1, 2, 3, 4} \ {0, 3, 4} == {1, 2}. This operation should be performed quite often for different sets, so I thought that I could create a set with all numbers ({0, 1, 2, 3, 4} in this case) and use std::set_difference to calculate those differences.

like image 979
Max Avatar asked Oct 22 '13 22:10

Max


2 Answers

To solve your actual problem: std::set_difference has less to do with std::set than you might expect. You can use any pair of iterators as the first two parameters of set_difference provided they return the values in order. There is no particular benefit in it being a set.

So for example the begin/end iterators of a std::vector containing the values 0 ... n-1 in order would work, or a pair of boost::counting_iterator:

std::set result;
std::set_difference(
    boost::counting_iterator<int>(0), boost::counting_iterator<int>(n),
    s1.begin(), s1.end(),
    std::inserter(result, result.end())
);

The output doesn't need to be a set either, you could just as well use a vector with back_inserter.

To solve what you asked: it doesn't make sense to try to use iota on a set. iota changes the values contained in a range, by assigning new values to them. You can't assign to the values in a set.

If you want a set containing the numbers 0 ... n-1, then:

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

If someone told you that loops are for wusses and real C++ programmers use algorithms, then you can get iota involved if you really want:

std::set<int> s;
{
    std::vector<int> vec(n);
    std::iota(vec.begin(), vec.end(), 0);
    s.insert(vec.begin(), vec.end());
}

Unfortunately that's kind of inefficient. So if you love algorithms so much that you might as well marry them, then you can reach outside the standard libraries:

std::set<int> s(boost::counting_iterator<int>(0), boost::counting_iterator<int>(n));
like image 199
Steve Jessop Avatar answered Sep 28 '22 00:09

Steve Jessop


A slightly inelegant alternative:

set<int> s;
generate_n(inserter(s, s.end()), 10, [&]{ return s.size(); });
like image 42
mattnewport Avatar answered Sep 27 '22 23:09

mattnewport