固定范围和动态范围
在声明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 天,点击查看活动详情”