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



我想为我的API提供一些迭代器,这些迭代器自动调用所需的成员函数来直接迭代返回的值。更准确地说,将以下结构和主要功能视为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
}

显然,我可以迭代形状并直接将其写入代码中,或者编写一些std:for_each,在所提供的lamba中进行调用。

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

因此,我的问题是如何用最少的样板来解决这个问题。我应该使用一些boost迭代器还是从一些std::iterator继承作为这些自由函数的返回值?我希望有一个简单的方法,我已经监督。因为上面提到的选项似乎很冗长。如果有一个相当通用的,那也太好了。因为在不重复迭代器样板的情况下,将其用于多个类会很好。

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

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

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

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

这是的演示

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

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类,并将其用于所有不同的函数(比如向它传递对每个形状调用的std::function(。

我想我已经通过上面给出的有用提示弄明白了。幸亏cigien、mattlangford和mooning duck。

使用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

由于最初的transformview版本也很有表现力,我认为area(…(解决方案更容易理解。

请参阅https://godbolt.org/z/Gff8K9cr8.

最新更新