如何创建调用成员函数的类似 stl 的迭代器

How to create an stl-like iterator which calls member functions

我想为我的 API 提供一些迭代器,这些迭代器会自动调用所需的成员函数以直接迭代 returned 值。更准确地说,将以下结构和主要函数视为 MVE:


struct Shape
{
  double getArea()
  {
  return ....//calculate area;
  }

  double getVolume()
  {
  return ....//calculate volume;
  }

  std:pair<double,double> getVolumeAndArea()
  {
  return std:make_pair...;
  }

// some data members
}

现在我有一个形状矢量,我想按以下方式使用它:


int main(){

std:vector<Shape> shapes;
//fill shapes

for(const auto area : area(shapes))
  // do something with area


for(const auto volume : volume(shapes))
  // do something with volume


for(const auto [volume, area] : volumeAndArea(shapes))
  // do something with volume and area
}

显然,我可以遍历形状并直接在代码中编写它,或者在提供的 lamba 调用发生的地方编写一些 std:for_each

尽管如此,我认为我想到的方法对于潜在用户来说不那么冗长。

因此,我的问题是如何用最少的样板解决这个问题。我应该使用一些 boost 迭代器还是从一些 std::iterator 继承这些自由函数的 return 值?我希望有一种我监督过的简单方法。由于提到的选项看起来很冗长。拥有这个相当通用的东西也很好。因为在不重复迭代器样板的情况下将它用于多个 类 会很好。

此外,我并不局限于旧的 c++ 版本,因此,也欢迎一个不错的 c++20 解决方案。

因此,有人可以给我一个很好的例子或最佳实践建议,告诉我如何以现代方式做到这一点吗?我有信心自己锻炼细节,因为我不想要求一个完整的解决方案。

在 C++20 中,范围版本的算法可以对成员进行投影,这几乎可以让您获得所需的易用性,语法略有不同

std::ranges::for_each(shapes, [](const auto & area) {
    // do something with area
}, &Shape::getArea);
// ^_____________^ projection on member

这是一个demo

这里是一个可能的实现,它使用迭代器而不是任何范围(特别是针对区域的情况):

class area {
private:
    class iterator {
    public:
        // needed so that it acts like a std::iterator
        using difference_type = int;
        using value_type = double;
        using reference = double;
        using pointer = std::add_pointer<value_type>;
        using iterator_category = std::forward_iterator_tag;

        double operator*() const {
            // compute the area when accessing the value of the iterator
            return shapes_[index_].getArea();
        }

        iterator &operator++() {
            index_++;
            return *this;
        }

        bool operator==(const iterator &rhs) const { return index_ == rhs.index_; }
        bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
        bool operator<(const iterator &rhs) const { return index_ < rhs.index_; }

        iterator(size_t index, const std::vector<Shape>& shapes) : index_(index), shapes_(shapes) {}

    private:
        size_t index_;
        const std::vector<Shape>& shapes_;
    };

public:
    area(const std::vector<Shape>& shapes) : shapes_(shapes) {}
    iterator begin() const { return iterator{0, shapes_}; }
    iterator end() const{ return iterator{shapes_.size(), shapes_}; }

private:
    const std::vector<Shape>& shapes_;
};

...

int main() {
    std::vector<Shape> shapes;
    for (const double area : area(shapes)) {

    }
}

范围看起来容易多了!

对于它的价值,您可能可以在此处提取 iterator class 并将其重新用于所有不同的功能(例如将其传递给调用的 std::function每个形状)。

我想我已经通过上面给出的有用提示弄明白了。谢谢 , and .

使用 std::ranges::transform_view 和投影可以很好地表达以下内容

using std::ranges::transform_view;
for(const auto area : transform_view(shapes, &Shape::getArea ))
        std::cout << area << "\n";

另见 https://godbolt.org/z/3xqn5bdWE

要同时提供一种类似于 API 的解决方案,可以将其包装成:

auto area(std::vector<Shape>& shapes)
{
    using std::ranges::transform_view;
    return transform_view(shapes, &Shape::getArea);
}

然后可以用作

for(const auto area :  area(shapes))
   //do something with area

因为最初的 transform_view 版本也很有表现力,我认为 area(...) 解决方案更容易理解。

https://godbolt.org/z/Gff8K9cr8