网站首页 > 博客文章 正文
许多脚本语言都包含了一个用于将两个序列打包在一起的函数。典型的 zip 操作将两个输入序列并返回每个输入中每个位置的一对值:
考虑两个序列的情况——它们可以是容器、迭代器或初始化列表:
我们想要将它们打包在一起,制作一个新的序列,其中包含来自前两个序列的元素对:
在这个食谱中,我们将使用迭代器适配器来完成这项任务。
如何做到这一点…
在这个食谱中,我们将构建一个 zip 迭代器适配器,它接受两个相同类型的容器,并将值打包到 std::pair 对象中:
在我们的 main() 函数中,我们想用两个向量调用我们的适配器:
int main() {
vector<std::string> vec_a {"Bob", "John", "Joni"};
vector<std::string> vec_b {"Dylan", "Williams", "Mitchell"};
cout << "zipped: ";
for(auto [a, b] : zip_iterator(vec_a, vec_b)) {
cout << format("[{}, {}] ", a, b);
}
cout << '\n';
}
这允许我们使用 zip_iterator 代替单独的向量迭代器。
我们期望的输出如下:
zipped: [Bob, Dylan] [John, Williams] [Joni, Mitchell]
我们的迭代器适配器在一个名为 zip_iterator 的类中。我们将从一些类型别名开始,以方便使用:
template<typename T>
class zip_iterator {
using val_t = typename T::value_type;
using ret_t = std::pair<val_t, val_t>;
using it_t = typename T::iterator;
}
这些允许我们方便地定义对象和函数。
我们的迭代器不存储任何数据。我们只存储目标容器的 begin() 和 end() 迭代器的副本:
it_t ita_{};
it_t itb_{};
// for begin() and end() objects
it_t ita_begin_;
it_t itb_begin_;
it_t ita_end_;
it_t itb_end_;
ita_ 和 itb_ 是来自目标容器的迭代器。其他四个迭代器用于为 zip_iterator 适配器生成 begin() 和 end() 迭代器。
我们还有一个私有构造函数:
// private constructor for begin() and end() objects
zip_iterator(it_t ita, it_t itb) : ita_{ita}, itb_{itb} {}
这稍后用于构造专门用于 begin() 和 end() 迭代器的适配器对象。
在 public 部分,我们从迭代器特性类型定义开始:
public:
using iterator_concept = std::forward_iterator_tag;
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<val_t, val_t>;
using difference_type = long int;
using pointer = const val_t*;
using reference = const val_t&;
构造函数设置所有私有迭代器变量:
zip_iterator(T& a, T& b) :
ita_{a.begin()},
itb_{b.begin()},
ita_begin_{ita_},
itb_begin_{itb_},
ita_end_{a.end()},
itb_end_{b.end()}
{}
我们定义了最少的运算符重载,以与前向迭代器一起工作:
zip_iterator& operator++() {
++ita_;
++itb_;
return *this;
}
bool operator==(const zip_iterator& o) const {
return ita_ == o.ita_ || itb_ == o.itb_;
}
bool operator!=(const zip_iterator& o) const {
return !operator==(o);
}
ret_t operator*() const {
return { *ita_, *itb_ };
}
最后,begin() 和 end() 函数返回相应的迭代器:
zip_iterator begin() const { return zip_iterator(ita_begin_, itb_begin_); }
zip_iterator end() const { return zip_iterator(ita_end_, itb_end_); }
这些通过存储的迭代器和私有构造函数变得简单。
现在让我们扩展我们的 main() 函数进行测试:
int main() {
vector<std::string> vec_a {"Bob", "John", "Joni"};
vector<std::string> vec_b {"Dylan", "Williams", "Mitchell"};
cout << "vec_a: ";
for(auto e : vec_a) cout << format("{} ", e);
cout << '\n';
cout << "vec_b: ";
for(auto e : vec_b) cout << format("{} ", e);
cout << '\n';
cout << "zipped: ";
for(auto [a, b] : zip_iterator(vec_a, vec_b)) {
cout << format("[{}, {}] ", a, b);
}
cout << '\n';
}
这给我们带来了我们想要的输出:
vec_a: Bob John Joni
vec_b: Dylan Williams Mitchell
zipped: [Bob, Dylan] [John, Williams] [Joni, Mitchell]
它是如何工作的…
打包迭代器适配器是迭代器抽象灵活性的一个例子。我们可以取两个容器的迭代器,并在一个大的聚合迭代器中使用它们。让我们来看看这是如何工作的。
zip_iterator 类的主要构造函数接受两个容器对象。为了本次讨论的目的,我们将这些对象称为目标对象。
zip_iterator(T& a, T& b) :
ita_{a.begin()},
itb_{b.begin()},
ita_begin_{ita_},
itb_begin_{itb_},
ita_end_{a.end()},
itb_end_{b.end()}
{}
构造函数从目标 begin() 迭代器初始化 ita_ 和 itb_ 变量。这些将用于导航目标对象。目标 begin() 和 end() 迭代器也被保存供以后使用。
这些变量在私有部分定义:
it_t ita_{};
it_t itb_{};
// for begin() and end() objects
it_t ita_begin_;
it_t itb_begin_;
it_t ita_end_;
it_t itb_end_{};
it_t 类型被定义为目标迭代器类的类型:
using val_t = typename T::value_type;
using ret_t = std::pair<val_t, val_t>;
using it_t = typename T::iterator;
其他别名类型是 val_t,表示目标值的类型,以及 ret_t,表示返回对的类型。这些类型定义在整个类中用于方便。
begin() 和 end() 函数使用只初始化 ita_ 和 itb_ 值的私有构造函数:
zip_iterator begin() const { return zip_iterator(ita_begin_, itb_begin_); }
zip_iterator end() const { return zip_iterator(ita_end_, itb_end_); }
私有构造函数如下所示:
// private constructor for begin() and end() objects
zip_iterator(it_t ita, it_t itb) : ita_{ita}, itb_{itb} {}
这是一个接受 it_t 迭代器作为参数的构造函数。它只初始化 ita_ 和 itb_,以便它们可以在比较运算符重载中使用。
类的其余部分表现得像一个普通的迭代器,但它操作的是来自目标类的迭代器:
zip_iterator& operator++() {
++ita_;
++itb_;
return *this;
}
bool operator==(const zip_iterator& o) const {
return ita_ == o.ita_ || itb_ == o.itb_;
}
bool operator!=(const zip_iterator& o) const {
return !operator==(o);
}
解引用运算符返回一个 std::pair 对象(ret_t 是 std::pair<val_t, val_t> 的别名)。这是从迭代器检索值的接口。
ret_t operator*() const {
return { *ita_, *itb_ };
}
还有更多…
zip_iterator 适配器可以用来轻松地将对象打包到 map 中:
map<string, string> name_map{};
for(auto [a, b] : zip_iterator(vec_a, vec_b)) {
name_map.try_emplace(a, b);
}
cout << "name_map: ";
for(auto [a, b] : name_map) {
cout << format("[{}, {}] ", a, b);
}
cout << '\n';
如果我们将这段代码添加到 main() 中,我们得到以下输出:
name_map: [Bob, Dylan] [John, Williams] [Joni, Mitchell]
猜你喜欢
- 2024-10-12 C++核心准则T.24:用标签类或特征区分只有语义不同的概念
- 2024-10-12 用苹果发布会方式打开C++20(苹果在哪开发布会)
- 2024-10-12 C++核心准则T.25:避免互补性约束(规矩是一种约束,一种准则)
- 2024-10-12 C++核心准则T.21:为概念定义一套完整的操作
- 2024-10-12 C++核心准则T.5:结合使用泛型和面向对象技术应该增强效果
- 2024-10-12 C++经典书籍(c++相关书籍)
- 2024-10-12 C++一行代码实现任意系统函数Hook
- 2024-10-12 C++核心准则T.11:只要可能就使用标准概念
- 2024-10-12 C++核心准则T.48:如果不能用概念,用enable_if
- 2024-10-12 C++核心准则T.13:简单、单类型参数概念使用缩略记法更好
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)