伪造插入器是一种好习惯吗?

时间:2013-09-19 14:13:27

标签: c++ algorithm stl

我们被教导创建函数对象以使用算法。

有些算法可以调用operator(),例如:

  • 的for_each
  • find_if
  • 的remove_if
  • max_element
  • count_if

这些函数对象通常应该从unary_functionbinary_function继承,表现得像函数谓词等。

但书籍通常不会展示创建OutputIterators的示例:

e.g。遍历函数的输出 std::set_intersection(),我必须提供目标容器, 然后然后遍历结果:

std::vector<int> tmp_dest;

std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        std::back_inserter(tmp_dest));

std::for_each( tmp_dest.begin(), tmp_dest.end(), do_something );
int res = std::accumulate( tmp_dest.begin(), tmp_dest.end(), 0 );

但是认为有时使用每个算法的值而不先存储它们会更有效,例如:

std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        do_something );

Accumulator accumulate(0);  // inherits from std::insert_iterator ?
std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        accumulate );
  • 我们通常应该创建像 Accumulator 这样的类吗?
  • 它的设计应该是什么样的?
  • 它应该继承什么? Accumulator 可以从insert_iterator继承,但它实际上不是迭代器(例如它没有实现operator++()

有哪些被广泛接受的做法?

5 个答案:

答案 0 :(得分:6)

如果您想要一个输出迭代器,为每个接收到的值调用您自己的函数,请使用Boost.Iterator's function_output_iterator

答案 1 :(得分:4)

我没有看到这个问题的根本问题,只要未来的维护者明白这些代码是如何工作的以及它正在做什么。

我可能不会从任何标准类继承这样的操作(除了给它output_iterator_tag之外)。由于我们正在处理模板,因此我们不需要处理父接口。

但请记住,你的语句(eg it does not implement operator++() )似乎不正确:无论你输入什么,因为“输出迭代器”需要满足输出迭代器的要求,包括可复制,取消引用 - 分配,可递增。无论您传递的对象类型是什么,都需要满足这些要求。

答案 2 :(得分:3)

我对此的看法是使用Boost(也显示了set_difference的Boost Range算法版本,尽管是偏离主题的):

#include <set>
#include <boost/range/algorithm.hpp>
#include <boost/function_output_iterator.hpp>
#include <cassert>

void do_something(int) {}

int main()
{
    const std::set<int> 
         src1 { 1,2,3 }, 
         src2 { 1,9 };

    unsigned total = 0;

    boost::set_difference(src1, src2, 
                boost::make_function_output_iterator([&](int i) 
                { 
                    total += i*i; 
                }));

    assert(total == 13); // 2*2 + 3*3
}

查看 Live On Coliru

答案 3 :(得分:2)

采用输出迭代器的算法的目标是由输出迭代器表示的值序列。他们使用迭代器有两个原因:

  1. 结果很可能存储在其他地方,即迭代器很有用。
  2. 协议要求每个职位只写一次。这比函数调用接口更具有修复性,即有额外的保证。
  3. 对于某些算法,提供了两个版本,一个具有函数调用接口,另一个具有迭代器接口。例如,这是std::for_each()std::copy()之间的差异。

    在任何情况下,如果您只需要一个需要输出迭代器的函数,那么让其他迭代器操作为no-ops并在赋值给*it时调用该函数:this创建一个完美的valud输出迭代器。

答案 4 :(得分:1)

以下作品:

#include <cassert>
#include <algorithm>

class AccumulatorIterator
{
public:
    explicit AccumulatorIterator(int initial) : value(initial) {}

    AccumulatorIterator& operator = (int rhs) { value += rhs; return *this; }
    AccumulatorIterator& operator *() { return *this; }

    AccumulatorIterator& operator ++() { return *this; }
    operator int() const { return value; }
private:
    int value;
};

int main() {
    int first[] = {5,10,15,20,25};
    int second[] = {50,40,30,20,10};

    std::sort(std::begin(first), std::end(first));   //  5 10 15 20 25
    std::sort(std::begin(second), std::end(second)); // 10 20 30 40 50

    const int res = std::set_intersection (std::begin(first), std::end(first),
        std::begin(second), std::end(second), AccumulatorIterator(0));

    assert(res == 10 + 20);
    return 0;
}