使用set_union和set_intersection时出错

时间:2011-04-14 14:49:30

标签: c++ intersection set

我有两套,我正在尝试做一个联合(我在做交叉时得到同样的错误)。这是错误:

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

代码片段(如果我用 - &gt;注释掉该行,那么代码编译并且我的工作方式可以正常工作):

    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++;
    }

我确信这是愚蠢的,但我有点迷茫。我认为代码片段应该足够了,但我可以提供其他所需的东西。感谢。

2 个答案:

答案 0 :(得分:6)

这是C ++,而不是Java [edit:或.NET]。你几乎肯定想要替换(例如):

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

只是:

set<Line *> newSet;

...或者,更好的是,可能只是:

set<Line> newSet;

虽然根据您发布的代码确定无法确定,但您的leftright也不应该在指针中处理 - 如果他们'要做任何类似的事情,参考可能更有意义(但是,正如我所说,基于你发布的内容,不可能肯定地说)。

一旦你完成了这个,就会遇到一个小问题:set(或multisetmapmultimap)上的“正常”迭代器是真的是一个const_iterator。一旦将某些内容插入到关联容器中,就不允许更改它,因为这可能会破坏集合的不变量(被排序)。如果要更改现有项,则需要从包含中删除if,进行更改,然后将更改的对象插回容器中。在您的情况下,您只是插入新项目,因此您需要insert_iterator

由于您不打算修改leftright,因此您也可以将其视为const

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

如果您决定自己模拟set_union,可以执行以下操作:

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

编辑:

您通常希望将迭代器传递到容器中,而不是传递指向容器的指针。例如,要打印出内容,您显然现在有类似的内容:

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

它可能有更多的格式等等,但目前我们将忽略这些细节并假设它就这么简单。要直接从您选择的容器中写入数据,通常需要一个接受任意类型迭代器的模板:

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

然而,我们可以更进一步,并将输出指定为迭代器:

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

你可以多走一步,并允许用户指定要在项目之间使用的分隔符,而不是总是使用'​​\ n',但在那时,你只是要复制已经存在的东西。标准库 - std::copystd::ostream_iterator的组合,这就是你可能想要在现实中处理这个问题的方法:

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

但请注意,就标准库而言,ostream_iterator只是另一个迭代器。如果您打算打印出leftright的联合,您甚至可以跳过创建一个集合以保存该联合,并直接将其打印出来:

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

ostream_iterator写入文件而不是将内容放入正常集合这一事实与标准库完全无关。它有几类迭代器,可以将输出写入任何迭代器,模拟正确的类。

现在,我可能会开枪,可能会说 - 在将数据写入控制台之前,可能需要对数据进行其他处理。我的观点并不是你必须直接将联合写入标准输出,而只是在打印出来之前你不必将其写入其他集合。

答案 1 :(得分:2)

set迭代器不是输出迭代器。使用此:

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

另外,为什么要填newSet?在联合/交叉点之后保持原样,或者联合/交叉将毫无意义。

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++;
}