set_difference,set_intersection和set_union的原位版本

时间:2017-06-04 22:11:53

标签: c++ algorithm stl

我实现了set_unionset_intersectionset_difference的版本,它们采用排序容器和排序范围(不得在容器内),并写入操作结果进入容器。

template<class Container, class Iter>
void assign_difference(Container& cont, Iter first, Iter last)
{
    auto new_end = std::set_difference( // (1)
        cont.begin(), cont.end(), first, last, cont.begin());
    cont.erase(new_end, cont.end());
}

template<class Container, class Iter>
void assign_intersection(Container& cont, Iter first, Iter last)
{
    auto new_end = std::set_intersection( // (2)
        cont.begin(), cont.end(), first, last, cont.begin());
    cont.erase(new_end, cont.end());
}

template<class Container, class Iter>
void assign_union(Container& cont, Iter first, Iter last)
{
    auto insert_count = last - first;
    cont.resize(cont.size() + insert_count); // T must be default-constructible
    auto rfirst1 = cont.rbegin() + insert_count, rlast1 = cont.rend();
    auto rfirst2 = std::make_reverse_iterator(last);
    auto rlast2 = std::make_reverse_iterator(first);
    rlast1 = std::set_union( // (3)
        rfirst1, rlast1, rfirst2, rlast2, cont.rbegin(), std::greater<>());
    cont.erase(std::copy(rlast1.base(), cont.end(), cont.begin()), cont.end());
}

目标是:

  • 如果容器具有容纳结果的容量,则不执行分配。
  • 否则,只执行一次分配以使容器具有保存结果的能力。

正如您在标记为(1),(2)和(3)的行中所看到的,同一容器用作那些STL算法的输入和输出。假设这些STL算法的通常实现,这段代码可以工作,因为它只写入已经处理过的容器部分。

正如评论中指出的那样,标准不能保证这一点。 set_unionset_intersectionset_difference要求结果范围与其中一个输入范围不重叠。 但是,是否存在破坏代码的STL实现?

如果您的答案是肯定的,请提供三种使用过的STL算法之一的符合规定的实施方案。

2 个答案:

答案 0 :(得分:1)

符合要求的实现可以检查set_intersection的参数1和5是否相等,以及它们是否为您的硬盘格式。

如果违反要求,程序的行为不受标准约束;你的程序生病了。

在某些情况下,UB可能值得冒风险和降低成本(审核所有编译器更改和程序集输出)。我不明白这一点;写你自己的。当您违反要求时,std库提出的任何花哨的优化都可能导致问题,并且您已经注意到天真的实现很简单。

答案 1 :(得分:0)

根据经验,我使用不要写在你正在迭代的容器上。一切都会发生。一般来说,这很奇怪。

正如@Yakk所说,这听起来很糟糕。就是这样。从你的代码库中删除的东西安然入睡。

如果你真的需要这些函数,我建议你自己编写内部循环(例如:std::set_intersection的内部),以便处理算法运行所需的约束。

我不认为寻求STL实施它不起作用是正确的方法。它听起来不像是一个长期的解决方案。从长远来看:标准应该是您的参考,正如有人已经指出的那样,您的解决方案似乎没有正确处理它。

我的2美分