固定范围和动态范围

431 阅读2分钟

固定范围和动态范围

在声明span时,可以在指定的固定数量的元素之间进行选择,也可以保留元素的数量,直到明确span引用哪些元素为止。

具有指定固定数量元素的span称为具有固定范围的span。

它是用元素类型和大小声明的(比如std::array<>)。

std::span<int,5>

//具有 5 个元素的固定范围的跨度的类型

对于这样的跨度,成员函数 size() 始终生成指定为类型一部分的大小。此处不可调用默认构造函数(除非范围为 0)。

元素数量可能发生变化的范围称为具有动态范围的范围。

元素的数量取决于 span 引用的元素序列,并且可能会通过使用赋值运算符进行更改。

但是,没有其他方法可以更改范围(元素数)。

对于这两种情况,让我们先看一个例子。

使用实例使用固定Extent的Span

下面是使用具有固定范围的跨度的第一个示例:

#include <iostream>
 #include <string> 
 #include <vector> 
 #include <ranges>
 #include <algorithm>
 #include <span> 
 #include <cassert>

template<typename T,std::size_t Sz> 

void printSpan(std::span<T,Sz> sp) 
{

	for (const auto& elem : sp) {

		std::cout<<<<elem<<"\""; 
		}

		std::cout <<'\n'; 
		
	}

int main() 

{

	std::vector<std::string> vec{"New York", "Tokyo", "Rio", "Berlin","Sydney"};

	assert(vec.size()>=3);

	std: :span<const std::string, 3> sp3{vec.begin(),3}; 
	
	std::cout <<"first 3:";

	printSpan(sp3);

	

	std::ranges::sort(vec);

	std::cout <<"first 3 after sort():"; 
	
	printSpan(sp3);
	
	
	auto oldCapa = vec.capacity();

	vec.push_back("Cairo");

	if (oldCapa != vec.capacity()) {
	
		sp3=std::span<std::string, 3>{vec.begin(),3};

		}

	std::cout <<"first 3:"; 
	
	printSpan(sp3);



	sp3 = std::span<std::string,3>{vec.end() - 3, vec.end()}; 
	
	std::cout <<"last 3:";

	printSpan(sp3); 

	printSpan(std::span{vec}); 
	
}

程序的输出如下所示

first 3: "New York" "Tokyo" "Rio"

first 3 after sort(): "Berlin" "New York" "Rio" first 3: "Berlin" "New York" "Rio"

last 3: "Sydney" "Tokyo" "Cairo"

vec: "Berlin" "New York" "Rio" "Sydney" "Tokyo" "Cairo"

让我们一步一步地看一下这个例子。

接下来,我们打印span,并将其传递给一个通用的打印函数:

printSpan(sp3);

print 函数可以处理任何跨度(只要定义了元素的输出运算符):

template<typename T, std::size_t Sz>

void printSpan(std::span<T, Sz> sp) {

for (const auto& elem : sp) {

std::cout <<'"'<< elem << "\""; }

std::cout <<'\n'; }

如您所见,跨度是按值传递的。这是推荐的方法,因为在内部,范围只是一个指针和一个大小。在函数内部,我们使用基于范围的 for 循环来迭代跨度的元素。

这是可能的,因为 span 提供了 begin() 和 end() 的迭代器支持。

但是,请注意:无论我们按值还是通过常量引用传递跨度,在函数内部,只要元素未声明为 const,我们仍然可以修改元素。

处理引用语义

接下来,我们对 span 引用的元素进行排序(我们在这里使用新的 std::ranges::sort,它将容器作为一个整体):

std::ranges::sort(vec);

由于 span 具有引用语义,因此此排序也会影响 span 的元素:它们也会被排序。如果我们没有 const 元素的跨度,我们也可以为跨度调用 sort()。

在这种情况下,我们只会对向量中的前 3 个元素进行排序:

std::vector<std::string> vec{"New York", "Rio", ...};

std::string, 3> span3{vec.begin(), 3};

std::ranges: :sort(span3); 

接下来,我们将一个新元素插入到包含跨度引用的元素的向量中。

由于 span 的引用语义,这是我们必须非常小心的事情,因为如果向量分配新的内存,它会使所有迭代器和指向其元素的指针无效。

因此,重新分配也会使引用向量元素的跨度无效。跨度是指不再存在的元素。出于这个原因,我们在插入之前和之后仔细检查容量(分配了 whnich 内存的最大元素数)。如果它发生了变化,我们重新初始化 span 以引用新内存中的前三个元素:

auto oldCapa = vec.capacity(); 

vec.push_back("Cairo");

if (oldCapa != vec.capacity()){

	span3 = std::span<std::string,3>{vec.begin(),3}; 
	
}

我们只能执行这种重新初始化,因为跨度本身不是常量。


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