创建外部元素的视图

494 阅读2分钟

用于创建视图的表特性及其适配器列出了用于创建视图的特性和相应的适配器。

一些视图引用外部元素(因此是换行范围),而另一些视图自己创建一个值序列。

我们总是倾向于使用适配器std::views::al1()而不是类型std::ranges::ref view<>

然而,对于某些视图类型,例如基本的istream view,这是不可能的。注意使用的不同命名空间:

  • span 和 string_view 位于命名空间 std 中。

  • 所有适配器都在命名空间std::views中。

  • 所有其他视图类型都在命名空间std::ranges中。

视图的基类

所有标准视图都派生自类 std::ranges::view_interface<viewType>,该类从类 std::ranges::view_base 派生:

  • 根类std::ranges::view base为空,仅确保派生类对std::ranges::view概念建模(为此,它初始化std::ranges::enable view<>true的视图类型)。

  • 类模板std::ranges::view interface<>引入了几个基于派生视图类型begin()end()定义的基本成员函数,它作为模板形参传递给这个基于类。

当你定义你自己的视图类型时,你应该用你自己的类型作为参数从view interface<>派生它。例如

template<typename T> 

class MyView : public std::ranges::view_interface<MyView<T>> { 

	public:

	...begin()...;

	...end()...; 
	
	...
	
	};
	
	

创建外部元素的视图

std::ranges::subrange

类模板std::ranges::subrange<>定义范围元素的视图,该范围元素要么作为单个参数传递,要么定义为一对开始迭代器和迭代器或哨兵。

视图本身通过存储开始和结束/哨兵在内部表示元素。

子范围视图的主要用例是将一对开始迭代器和哨兵迭代器(结束迭代器)转换为一个对象。例如

void foo(auto beg, auto end) 

	{
		std::ranges::subrange rg{beg,end}; 

	}

两个范围适配器也可以创建一个子范围

  • std::views::a11 (rg),如果rg还不是一个视图,它会创建一个子范围。可以用来将容器作为轻量级对象传递。
  • std::views:: counts (beg, sz),如果beg不指向连续内存(对于连续内存,counts()创建一个span),则创建一个以迭代器beg开头的sz元素子范围。

构造函数接受szHint,允许程序员将一个未大小的范围转换为一个有大小的子范围。

如果传递szHint,而范围没有已知的大小(例如,它是一个转发列表),则使用提示作为大小。如果指定了错误的大小,这是未定义的行为。

迭代器不引用此视图。相反,它们指的是基础范围。因此,子范围是借用的范围。

子域的类元组接口

std::ranges::subrange也有一个类似元组的接口来支持结构化绑定(随着c++ 17引入到c++)。因此,您可以轻松地初始化一个begin迭代器和一个sentinel (end迭代器),如下所示

auto [beg, end] = std::ranges::subrange(col1);

使用Subranges协调数组类型

子范围的类型只取决于迭代器的类型(以及是否提供size())。这可以用来协调数组的类型,而不会丢失任何信息。

参考以下示例:

int arr1[5] = {...};

int arr2[10] ={...};

std::array<int, 15> arr3 = {...}; 

std::array<int,20> arr4 = {...};

将arr1到ar4传递给子范围会产生相同的类型。这对于避免原始数组和具有相同元素类型但大小不同的std::array的泛型代码的多次实例化和代码生成非常有用。

如果函数模板被调用,并且调用没有被优化,那么函数模板只在传递子范围时实例化一次

template<std::ranges::input_range T>

void foo(const T& coll) {

}

// 4 种不同类型的 4 个实例化:

foo(arr1);

foo(arr2); 

foo(arr3); 

foo(arr4);

// 实例化(全部采用 sume 类型):

foo(std::ranges::subrange{arr1}); 

foo(std::ranges::subrange{arr2}); 

foo(std: :ranges: : subrange{arr3}); 

foo(std::ranges::subrange{arr4});

如果只使用这些类型的begin()end(),也可以得到同样的效果。

std::ranges::ref_view

template std::ranges::ref view<>定义了一个简单引用范围的视图。

这样按值传递视图的效果就像按引用传递范围一样。

ref视图的主要用例是将容器转换为易于复制的轻量级对象。

范围适配器也可以创建一个引用视图

- std::views::a11(rg),如果rg还不是一个视图,它会创建一个ref视图。

您只能为左值(有名称的范围)创建视图。

std::vector coll{0,8,15}; 

...

std::ranges::ref_view v1{coll};//OK,refers to co11

std::ranges::ref_view v2{std::move(coll)};//ERROR 

std::ranges::ref_view v3{std::vector{0,8,15}};//ERROR

迭代器不引用此视图。相反,它们指的是基础范围。因此,引用视图是一个借用的范围。


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情