C++中带有lambdas的简单自定义迭代器

Simple customized iterator with lambdas in C++

本文关键字:简单 自定义 迭代器 lambdas C++      更新时间:2023-10-16

假设我有一个容器,它包含int,一个在包含Point的容器上工作的函数,并且有一个函数,给定一些int会给我它所代表的相应的Point(假设我已经在某个大的std::vector<Point>中索引了场景中的所有点)。如何创建一个简单(高效)的包装器来使用我的第一个容器而不复制其内容?

我想键入的代码是这样的:

template<typename InputIterator>
double compute_area(InputIterator first, InputIterator beyond) {
    // Do stuff
}
template<typename InputIterator, typename OutputIterator>
void convex_hull(InputIterator first, InputIterator beyond, OutputIterator result) {
    // Do stuff
}
struct Scene {
    std::vector<Point> vertices;
    foo(const std::vector<int> &polygon) {
        // Create a simple wraper with limited amount of mumbo-jumbo
        auto functor = [](int i) -> Point& { return vertices[polygon[i]]; });
        MagicIterator polyBegin(0, functor);
        MagicIterator polyEnd(polygon.size(), functor);
        // NOTE: I want them to act as random access iterator
        // And then use it directly
        double a = compute_area(polyBegin, polyEnd);
        // Bonus: create custom inserter similar to std::back_inserter
        std::vector<int> result;
        convex_hull(polyBegin, polyEnd, MagicInserter(result));
    }
};

所以,正如你所看到的,我正在寻找一些通用的东西。我也想过使用lambdas,但我对如何保持它的简单性和用户友好性有点困惑。

我建议Boost的Transform Iterator。下面是一个用法示例:

#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <cassert>
#include <functional>
struct Point { int x, y; };
template<typename It>
void compute(It begin, It end)
{
    while (begin != end) {
        begin->x = 42;
        begin->y = 42;
        ++begin;
    }
}
int main()
{
    std::vector<Point> vertices(5);
    std::vector<int> polygon { 2, 3, 4 };
    std::function<Point&(int)> functor = [&](int i) -> Point& { return vertices[i]; };
    auto polyBegin = boost::make_transform_iterator(polygon.begin(), functor);
    auto polyEnd = boost::make_transform_iterator(polygon.end(), functor);
    compute(polyBegin, polyEnd);
    assert(vertices[2].y == 42);
}

我没有完全理解关于自定义back_inserter的部分。如果存储在result向量中的类型与functor返回的类型相同,则标准库中的类型即可。否则,您也可以将其封装在transform_iterator中。

注意,函子存储在std::function中。Boost依赖于函数来定义typedef result_type,而lambdas没有。

我看到了两种方法。要么从boost::iterator_facade开始,然后编写"函数迭代器"类型。

或者,使用boost::counting_iterator迭代器或编写自己的迭代器(它们很容易),然后使用boost::transform_iteratorIndex迭代器映射到Point迭代器。

以上所有内容也可以直接编写。我会把它写成一个随机访问迭代器:它需要一些typedefs、++--+=-=-+s、比较以及*->才能正确定义。它有点像样板,上面的boost库只是让它成为一个无点样板(通过将样板本身包含在内)。

我已经为自己编写了一个版本,它将函数类型作为参数,然后将函数与索引一起存储。它使用索引推进/比较等,并使用函数类型取消引用。通过使函数类型为std::function<blah()>,我得到了它的类型擦除版本,通过使它成为lambda参数的decltype,或函子的类型,我得到一个更有效的版本。