将范围与动态范围结合使用

376 阅读2分钟

将范围与动态范围结合使用

作为具有动态范围的span的第一个示例,让我们仅使用具有动态范围的span来转换上一篇文章的的示例

上篇文章链接:固定范围和动态范围 - 掘金 (juejin.cn)

#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> sp{vec.begin(),3}; 
	
	std::cout << "first 3: ";

	printSpan(sp);


	std::ranges::sort(vec);

	std::cout << "first 3 after sort():"; 
	
	printSpan(sp);




	auto oldCapa = vec.capacity();

	vec.push_back("Cairo");

	if (oldCapa != vec.capacity()) { 
	
		sp= std::span{vec.begin(),3}; 
		
		}

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


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

	printSpan(sp); 
	
	std::cout <<"vec:";

	printSpan(std::span{vec}); 
	

	std::cout <<"sp:"; sp=vec;

	printSpan(sp); 
	
}

程序的输出如下:

 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"

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

同样,让我们逐步浏览该示例。

声明一个Span

在main()中,我们首先初始化一个span。但是,这次我们没有指定一个固定的范围

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

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

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

我们甚至可以在这里使用CTAD,这样张成的空间就可以推断出元素的类型

std::span sp3{vec.begin(), 3}; //deduces std::span<std::string>

结果是元素必须是字符串,但我们可以用不同数量的元素分配序列。

对于固定范围的跨度:

  • 程序员负责确保传递的元素序列是有效的。

  • 永远不要用(返回的)临时值初始化一个span。

  • 通过指定元素是 const std::string,我们不能通过跨度修改它们。

    • std::span<const std::string> sp3{vec.begin(), 3}; //元素不能被修改
    • const std::span<std::string> sp3{vec.begin(), 3}; //元素可以被修改
  • 只有通过添加限定符(如const),才能为span使用与引用元素不同的元素类型。

  • 你也可以使用data()来代替begin()。

传递和打印Span

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

printSpan(sp3);

您可能会惊讶于函数模板printSpan<>()仍然可以被调用,尽管它有一个用于span大小的模板参数

template<typename T, std::size_t Sz> 

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

它可以工作,因为std:: span是具有伪大小std::dynamic范围的span的快捷方式

std::span<int> sp; //相当于std::span<int, std::dynamic extent

事实上,类模板std::span<>的声明如下

namespace std {

	template<typename ElementType,size_t Extent = dynamic_extent> 

	class span{
		...
	}; 
}

这样,就可以提供printSpan<>()这样的函数模板,它既适用于固定的跨度,也适用于动态的跨度。当使用具有动态范围的span调用printSpan<>()时,std::dynamic extent作为size传递

std::span<int> sp; 


printSpan(sp);

再次注意,span是按值传递的,并且再次注意:在函数内部,我们仍然可以修改元素,只要它们没有声明为const。

将容器分配给Span

程序的其余部分与版本中具有固定范围的跨度一样。

然而,我们在最后增加了一件事:我们将向量作为一个整体分配给张成的空间,并将它打印出来

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

printSpan(sp);


sp=vec;

printSpan(sp);

在这里,您可以看到对span的赋值可以改变元素的数量。

使用具有固定范围和动态范围的span

固定范围和动态范围都有各自的好处。

指定固定大小允许我们更好地检测违规(在运行时甚至在编译时)。

例如,不能将元素数量错误的std::array<>分配给具有固定范围的span

std::vector vec{1, 2, 3};

std::array arr{1,2,3,4,5,6}; 

std::span<int, 3> sp3{vec};

std::span sp{vec};

sp3 =arr;

sp=arr;

使用具有动态区段的跨度提供了更大的灵活性

std::span<int> sp;

...

std::vector vec{1, 2, 3,4,5}; 

sp=vec;

sp = {vec.begin(), 3};


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