开发者

Error when using set_union and set_intersection

开发者 https://www.devze.com 2023-02-25 18:37 出处:网络
I have two sets and I\'m trying to do a union (I get the same error when doing an intersection).Here is the error:

I have two sets and I'm trying to do a union (I get the same error when doing an intersection). Here is the error:

error C3892: 'std::_Tree_const_iterator<_Mytree>::operator *' : you cannot assign to a variable that is const

Code snippet(if I comment out the line with the --> then the code compiles and my work around way of doing the union works fine):

    set<Line *>::iterator it;
    set<Line *> * newSet = new set<Line *>();
    leftLines = pLeft->getSet();
    rightLines = pRight->getSet();
 -->it = set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), newSet->begin());
    for(it = leftLines->begin(); it != leftLines->end(); it++)
    {
        newSet->insert(*it);
    }
    for(it = rightLines->begin(); it != rightLines->end(); it++)
    {
        newSet->insert(*it);
    }
    it = newSet->begin();
    while(it != newSet->end())
    {
        result->insert(*it);
        it++;
    }

I'm sure this is som开发者_高级运维ething silly but I'm kind of lost. I think that code snippet should be enough but I can provide whatever else is needed. Thanks.


This is C++, not Java [edit: or .NET]. You almost certainly want to replace (for example):

set<Line *> * newSet = new set<Line *>();

with just:

set<Line *> newSet;

...or, better still, probably just:

set<Line> newSet;

Although it's impossible to say for certain based on the code you've posted, there's a pretty fair chance that your left and right shouldn't be dealing in pointers either -- if they're going to do anything of the sort, a reference probably makes more sense (though, as I said, based on just what you've posted, it's impossible to say for sure).

Once you've done that, you run into a minor problem: a "normal" iterator over a set (or multiset, map or multimap) is really a const_iterator. Once you insert something into an associative container, you're not allowed to change it because that could destroy the collection's invariant (being sorted). If you want to change an existing item, you need to delete if from the contain, make the change, and insert the changed object back into the container. In your case, you're just inserting new items, so you want an insert_iterator.

Since you're not planning on modifying either left or right, you might as well treat them as const as well:

std::set_union(left.cbegin(), left.cend(), 
               right.cbegin(), right.cend(), 
               std::inserter(newSet, newSet.end()));

If you decide to simulate set_union on your own, you can do something like this:

std::set<Line> newSet(left.cbegin(), left.cend());  
std::copy(right.cbegin(), right.cend(), std::inserter(newSet, newSet.end()));

Edit:

Instead of passing around pointers to containers, you normally want to pass around iterators into the containers. For example, to print out the contents, you apparently now have something like:

void print_data(std::vector<Line *> const *data) { 
     for (int i=0; i<data->size(); i++)
         std::cout << *(*data)[i] << "\n";
}

It probably has more formatting and such, but for the moment we'll ignore those details and assume it's this simple. To write the data directly from a container of your choice, you normally want a template that will accept iterators of an arbitrary type:

template <class inIt>
void print_data(inIt begin, inIt end) { 
     while (begin != end) 
         std::cout << *begin++ << '\n';
}

We can, however, go a step further than that, and specify the output as an iterator as well:

template <class inIt, class outIt>
void print_data(inIt begin, inIt end, outIt dest) { 
    while (begin != end) {
        *dest++ = *begin++;
        *dest++ = '\n';
    }
}

You could go one more step, and allow the user to specify the delimiter to be used between the items, instead of always using '\n', but at that point, you'd just be duplicating something what's already in the standard library -- a combination of std::copy and an std::ostream_iterator, which is how you probably want to deal with this in reality:

std::copy(newSet.begin(), newSet.end(), 
          std::ostream_iterator<Line>(std::cout, "\n"));

Note, however, that as far as the standard library cares, an ostream_iterator is just another iterator. If you're just going to print out the union of left and right, you can skip even creating a set to hold that union, and just print it out directly:

std::set_union(left.cbegin(), left.cend(),
               right.cbegin(), right.cend(), 
               std::ostream_iterator<Line>(std::cout, "\n"));

The fact that an ostream_iterator writes to a file instead of putting things into a normal collection is entirely irrelevant to the standard library. It has a few classes of iterators, and can write output to any iterator that models the correct class.

Now, I may be jumping the gun, so to speak -- maybe need to do other processing on the data before you write it to the console. My point isn't that you necessarily have to write the union directly to standard output, but just that you don't necessarily have to write it to some other collection before you print it out.


set iterators aren't output iterators. Use this:

set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), inserter(*newSet, newSet->begin()));

Also, why're you filling newSet? Leave it as is after the union/intersection or the union/intersection will be pointless.

set<Line *>::iterator it;
set<Line *> newSet; // No need to `new` this
leftLines = pLeft->getSet();
rightLines = pRight->getSet();
set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), inserter(newSet, newSet.begin()));

// Assuming you really need the below code - you could likely just make an inserter directly on `result` instead of the copying.
it = newSet.begin();
while(it != newSet.end())
{
    result->insert(*it);
    it++;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号