如何创建调用成员函数的类似 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(...) 解决方案更容易理解。
我想为我的 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
每个形状)。
我想我已经通过上面给出的有用提示弄明白了。谢谢
使用 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(...) 解决方案更容易理解。