文章目录
- 一、引言
- 二、C++23与Ranges库背景知识
- 2.1 C++23概述
- 2.2 Ranges库回顾
- 三、`views::zip` 详解
- 3.1 功能与定义
- 3.2 使用场景
- 3.3 示例代码
- 四、`views::zip_transform` 详解
- 4.1 功能与定义
- 4.2 使用场景
- 4.3 示例代码
- 五、`views::zip` 与 `views::zip_transform` 的对比
- 5.1 功能差异
- 5.2 使用场景差异
- 5.3 代码复杂度差异
- 六、总结
一、引言
在C++的发展历程中,每一个新版本都会带来一系列令人期待的新特性,这些特性不仅提升了语言的性能和表达能力,还为开发者提供了更加便捷和高效的编程方式。C++23作为C++标准的一个重要版本,引入了许多实用的特性,其中 views::zip
和 views::zip_transform
(提案编号P2321R2)就是两个非常有价值的特性,它们与C++20引入的Ranges库紧密相关,为处理范围数据提供了新的视角和方法。
二、C++23与Ranges库背景知识
2.1 C++23概述
C++23是C++标准的下一个重要更新版本,虽然不如C++20那样具有颠覆性,但它依然带来了许多值得开发者关注的改进和新增特性。C++23在C++20的基础上进行了补充和优化,解决了一些细节问题,并引入了新的编程工具和方法,旨在进一步提升C++语言的功能和开发效率。
2.2 Ranges库回顾
C++20引入的Ranges库是一个重要特性,它彻底改变了我们处理序列数据的方式,提供了更富有表现力、更易组合的抽象。简单来说,Range就是一种可以遍历的序列,你可以把它想象成更智能、更灵活的数组或者容器。C++20引入了Ranges这个概念,让我们可以更方便地操作这些序列,例如,可以使用Ranges来过滤、转换、拼接序列等。
std::views
是C++20里提供的一系列工具函数,用来对序列进行各种变换。它可以帮助我们以一种非常直观的方式对序列进行操作,比如过滤、转换、切片等等。以下是一个简单的示例,展示了如何使用 std::views
来过滤出数组中的偶数,并将这些偶数加倍:
#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};auto result = numbers | std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * 2; });for (int n : result) {std::cout << n << ' ';}return 0;
}
在这个示例中,我们使用 std::views::filter
和 std::views::transform
对序列进行了处理,代码不仅简洁,而且非常直观。
三、views::zip
详解
3.1 功能与定义
std::ranges::views::zip
是C++23引入的一个范围适配器,它接受一个或多个 view
,并生成一个 view
,其第 i
个元素是由所有视图的第 i
个元素组成的类似元组的值。生成的视图的大小是所有适配的视图大小的最小值。
在标头 <ranges>
中定义如下:
template < ranges::input_range ... Views >
requires ( ranges::view < Views> && ...) && ( sizeof...(Views) > 0 )
class zip_view
: public ranges::view_interface <zip_view< Views...>>
(1) (自 C++23 起)namespace views {
inline constexpr /*unspecified*/ zip = /*unspecified*/;
}
(2) (自 C++23 起)调用签名
template < ranges::viewable_range... Rs >
requires /* 见下文 */
constexpr ranges::view auto zip( Rs&&... rs ) ;
(自 C++23 起)
zip_view
始终实现 input_range
,并且若所有适配的 view
类型均实现 forward_range
、bidirectional_range
、random_access_range
或 sized_range
时实现对应的概念。
3.2 使用场景
views::zip
适用于需要同时遍历多个范围的场景,例如,当你需要对多个数组或容器进行并行处理时,可以使用 views::zip
将它们组合在一起,然后进行统一的操作。
3.3 示例代码
#include <list>
#include <array>
#include <tuple>
#include <ranges>
#include <vector>
#include <string>
#include <iostream>void print(auto const rem, auto const& range)
{for (std::cout << rem; auto const& elem : range)std::cout << elem << ' ';std::cout << '\n';
}int main()
{auto x = std::vector{1, 2, 3, 4};auto y = std::list<std::string>{"α", "β", "γ", "δ", "ε"};auto z = std::array{'A', 'B', 'C', 'D', 'E', 'F'};print("Source views:", "");print("x: ", x);print("y: ", y);print("z: ", z);print("\nzip(x,y,z):", "");for (std::tuple<int&, std::string&, char&> elem : std::views::zip(x, y, z)){std::cout << std::get<0>(elem) << ' '<< std::get<1>(elem) << ' '<< std::get<2>(elem) << '\n';std::get<char&>(elem) += ('a' - 'A'); // modifies the element of z}print("\nAfter modification, z: ", z);
}
其运行的结果是:
Source views:
x: 1 2 3 4
y: α β γ δ ε
z: A B C D E Fzip(x,y,z):
1 α A
2 β B
3 γ C
4 δ DAfter modification, z: a b c d E F
四、views::zip_transform
详解
4.1 功能与定义
std::ranges::views::zip_transform
同样是C++23引入的一个范围适配器,它接受一个可调用对象和一个或多个 view
,并生成一个 view
,其第 i
个元素是将可调用对象应用于所有视图的第 i
个元素的结果。
在标头 <ranges>
中定义如下:
template< std::copy_constructible F, ranges::input_range... Views >
requires (ranges::view<Views> && ...) && (sizeof...(Views) > 0) &&std::is_object_v<F> && std::regular_invocable<F&, ranges::range_reference_t<Views>...> &&/*can-reference*/<std::invoke_result_t<F&, ranges::range_reference_t<Views>...>>
class zip_transform_view: public ranges::view_interface<zip_transform_view<F, Views...>>
(1) (since C++23)namespace views {inline constexpr /*unspecified*/ zip_transform = /*unspecified*/;
}
(2) (since C++23)Call signature
template< class F, ranges::viewable_range... Rs >
requires /* see below */
constexpr auto zip_transform( F&& f, Rs&&... rs );
(since C++23)
4.2 使用场景
views::zip_transform
适用于需要对多个范围的元素进行某种计算或转换的场景,例如,你可以使用它来计算两个数组对应元素的和、差、积等。
4.3 示例代码
#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> a = {1, 2, 3, 4};std::vector<int> b = {5, 6, 7, 8};auto result = std::views::zip_transform([](int x, int y) { return x + y; }, a, b);for (int val : result) {std::cout << val << ' ';}std::cout << '\n';return 0;
}
在这个示例中,我们使用 views::zip_transform
计算了两个向量对应元素的和,并将结果输出。
五、views::zip
与 views::zip_transform
的对比
5.1 功能差异
views::zip
主要用于将多个范围的元素组合成元组,方便同时遍历多个范围。它只是简单地将多个范围的元素打包在一起,不进行任何计算或转换。views::zip_transform
则在views::zip
的基础上,增加了对元素的计算或转换功能。它会将可调用对象应用于每个元组中的元素,并返回计算结果。
5.2 使用场景差异
- 当你只需要同时遍历多个范围,而不需要对元素进行额外的计算时,使用
views::zip
即可。 - 当你需要对多个范围的元素进行某种计算或转换时,使用
views::zip_transform
更加合适。
5.3 代码复杂度差异
views::zip
的代码相对简单,只需要将多个范围作为参数传递给views::zip
即可。views::zip_transform
除了需要传递多个范围外,还需要传递一个可调用对象,代码相对复杂一些。
六、总结
views::zip
和 views::zip_transform
是C++23中非常实用的两个特性,它们基于Ranges库,为处理多个范围的数据提供了更加便捷和高效的方式。views::zip
可以将多个范围的元素组合在一起,方便同时遍历;views::zip_transform
则可以对多个范围的元素进行计算或转换,进一步扩展了功能。
在实际开发中,我们可以根据具体的需求选择使用 views::zip
或 views::zip_transform
。同时,我们也可以将它们与其他Ranges库的工具函数结合使用,实现更加复杂的数据处理逻辑。随着C++23的不断普及和应用,这些新特性将逐渐得到更多的关注和使用,帮助开发者编写出更简洁、高效、易读的代码。