在泛型代码中使用span作为参数

553 阅读2分钟

在泛型代码中使用span作为参数

可以通过以下声明为所有span实现泛型函数

template<typename T,std::size_t Sz>

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

这甚至适用于具有动态区段的跨度,因为它们只是使用特殊值std::dynamic extent作为大小。

因此,在实现中,您可以如下方式处理固定和动态区段的差异


#ifndef SPANPRINT_HPP 
#define SPANPRINT_HPP 
#include <iostream> 
#include <span> 

template<typename T, std::size_t Sz>

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

{

	std::cout <<'[' << sp.size() <<"elems";
	
	if constexpr (Sz == std::dynamic_extent){ 
	
		std::cout << " (dynamic)";

} 

else{

	std::cout <<"(fixed)"; 
	
	}

std::cout<<;

for (const auto& elem : sp){ 

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

std::cout <<"]\n"; 

}


您还可以考虑将元素类型声明为const

 template<typename T, std::size_t Sz>

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

但是,请注意,这样就不能传递非const元素类型的span。

从非const类型到const类型的转换不会传播到模板(这是有原因的)。

缺乏类型推断和转换也不能将普通容器(如vector)传递给此函数。

您需要显式类型规范或显式转换

printSpan(vec);

//错误:模板类型推断在那里不起作用

printSpan(std::span{vec});

//OK

printSpan<int, std::dynamic_extent>(vec);

// OK(前提是它是一个向量)

因此,std::span<>不应该用作处理存储在连续内存中的元素序列的泛型函数的词汇表类型。

出于性能原因,您可以这样做

template<typename E>

void processSpan(std::span<typename E>) {

	...

//跨度特定实现

}

template<typename T>

void print(const T& t){

if constexpr (std::ranges::contiguous_range<T> t) {

	processSpan<std::ranges: :range_value_t<T>>(t); 
	
	}

else{

	...
//所有接口/范围的通用实现 }

}

使用跨度作为范围和视图

span是一个范围,可以在范围的所有算法和函数中使用。它甚至是一个借来的范围,这意味着您可以在生成迭代器的算法中使用临时span作为范围

std::vector<int> coll{25,42,2,0,122,5,7};

auto pos1 = std::ranges::find(std::span{coll.data(),3}, 42); 

// 没有悬空的 itera- tor

std::cout << *pos1 <<'\n';

但是,请注意,如果span引用一个临时对象,这是一个错误。

虽然返回一个指向已销毁临时对象的迭代器,但下面的代码仍可编译

auto pos2 = std::ranges::find(std::span{getData().data(),3},42); 

std::cout<<*pos2<<'\n';

// 运行时错误

span也是视图,并对std::ranges::view概念进行建模。


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