16

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

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

  • for_each
  • find_if
  • remove_if
  • 最大元素
  • count_if

这些函数对象通常应该继承自unary_functionor binary_function,以表现得像函数谓词等。

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

例如,要遍历函数的输出 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类吗?
  • 它的设计应该是什么样的?
  • 它应该继承什么? 累加器可以继承自insert_iterator,但它并不是真正的迭代器(例如,它没有实现operator++()

被广泛接受的做法是什么?

4

5 回答 5

6

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

于 2013-09-19T14:33:49.210 回答
4

只要未来的维护者清楚代码是如何工作的以及它在做什么,我认为这没有根本问题。

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

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

于 2013-09-19T14:31:52.000 回答
3

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

#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
}

在Coliru现场观看

于 2013-09-19T14:47:08.793 回答
2

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

  1. 结果很可能存储在其他地方,即迭代器很有用。
  2. 该协议要求每个位置只写一次。这比函数调用接口更具限制性,即有一个额外的保证。

对于某些算法,提供了两种版本,一种具有函数调用接口,另一种具有迭代器接口。例如,这就是 和 之间的std::for_each()区别std::copy()

在任何情况下,如果您只需要在需要输出迭代器的地方调用一个函数,只需让其他迭代器操作为无操作,并在分配给结果时调用该函数*it:这将创建一个完美的输出迭代器。

于 2013-09-19T15:16:19.917 回答
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;
}
于 2013-09-19T14:40:22.067 回答