最近我收到了在我的代码中使用span<T>
的建议,或者在使用span
的网站上看到了一些答案 - 据说某种容器。但是 - 我在C ++标准库中找不到类似的东西。
那么这个神秘的span<T>
是什么,为什么(或什么时候)如果它是非标准的话,使用它是个好主意?
答案 0 :(得分:196)
span<T>
是:
struct { T * ptr; size_t length; }
。以前称为array_view
,甚至早于array_ref
。
首先,当不使用它时:
std::sort
,std::find_if
,std::copy
以及所有这些超通用模板化函数。现在何时实际使用它:
使用
span<T>
(分别为span<const T>
)代替您拥有长度值的独立T*
(分别为const T*
)。因此,替换以下函数:void read_into(int* buffer, size_t buffer_size);
使用:
void read_into(span<int> buffer);
哦,跨度很棒!使用span
...
意味着您可以使用指针+长度/开始+结束指针组合,就像使用花哨的,拉皮条的标准库容器一样,例如:
for (auto& x : my_span) { /* do stuff */ }
std::find_if(my_span.begin(), my_span.end(), some_predicate);
...但绝对没有大多数容器类产生的开销。
让编译器有时为您做更多的工作。例如,这个:
int buffer[BUFFER_SIZE];
read_into(buffer, BUFFER_SIZE);
成为这个:
int buffer[BUFFER_SIZE];
read_into(buffer);
......这会做你想做的事。另见Guideline P.5。
是将const vector<T>&
传递给函数的合理替代方法。不再受到高强度C ++专家的谴责。
span
的方法将在#ifndef NDEBUG
... #endif
内有一些边界检查代码使用span
的动机更有动力,你可以在C++ core guidelines中找到它,但是你可以找到漂移。
它在标准库中 - 但仅限于C ++ 20。原因在于,它与目前的形式相比仍然很新,与C++ core guidelines项目一起构思,该项目自2015年以来才刚刚起步。(虽然评论者指出,它有早期历史。)
它是Core Guidelines支持库(GSL)的一部分。实现:
请注意,您可以将它与早期版本的语言标准一起使用 - C ++ 11和C ++ 14,而不仅仅是C ++ 17。
进一步阅读:您可以在C ++ 17,P0122R7之前的最终官方提案中找到所有细节和设计注意事项:Neal Macintosh和Stephan J. Lavavej的span: bounds-safe views for sequences of objects。虽然有点长。此外,在C ++ 20中,跨度比较语义发生了变化(在Tony van Eerd的this short paper之后)。
答案 1 :(得分:11)
span<T>
是这个
template <typename T>
struct span
{
T * ptr_to_array; // pointer to a contiguous C-style array of data
// (which memory is NOT allocated or deallocated
// by the span)
std::size_t length; // number of elements in the array
// Plus a bunch of constructors and convenience accessor methods here
}
它是围绕C样式数组的轻量级包装器,当C ++开发人员使用C库并希望将它们与C ++样式数据容器包装以实现“类型安全”和“ C ++ ishness”时,它们首选和“感觉”。 :)
@einpoklum在介绍span
是in his answer here方面做得很好。但是,即使在阅读了答案之后,对于新手来说,仍然很容易会遇到一系列问题,而这些问题并未得到充分回答,例如:
span
与C数组有何不同?为什么不只使用其中之一?似乎只是已知尺寸的那些之一... std::array
,span
与此有何不同?std::vector
也不像std::array
一样吗?span
?因此,在此方面有一些额外的说明:
他的答案的直接报价- 我的添加和括号内的注释以粗体显示,并且我强调斜体:
这是什么?
一个
span<T>
是:
- 内存中某处类型
T
的连续值序列的非常轻量级的抽象。- 基本上是一个单个结构
{ T * ptr; std::size_t length; }
,具有许多便捷方法。 (请注意,这与std::array<>
截然不同,因为span
通过指针键入std::array
启用了与T
相当的便捷访问器方法和类型T
的长度(元素数),而std::array
是一个实际的容器,其中包含一个或多个类型T
的值。)< / strong>- 一种非所有类型(即"reference-type"而不是“值类型”):它从不分配或取消分配任何东西,并且不保持智能指针还活着。
以前称为
array_view
,甚至更早称为array_ref
。
这些大胆的部分对人们来说是至关重要的,所以请不要错过它们或误读它们! span
不是结构的C数组,也不是类型为T
的C数组的结构加上数组的长度(实际上,这就是std::array
container 是),NOR也不是一种指向T
类型的指针结构的C数组加上长度,但它是包含一个单个字符的单个结构要键入T
的指针和长度,它是连续内存块中元素的数量(类型T
)指向类型T
的指针指向!! 这样,您使用span
添加的唯一开销就是用于存储指针和长度的变量以及任何方便的访问器span
提供的功能。
这是不喜欢std::array<>
,因为std::array<>
实际上为整个连续块分配了内存,而它是不喜欢std::vector<>
,因为std::vector
基本上只是一个{{ 1}}也会在每次填满并且尝试向其中添加其他内容时进行动态增长(通常会增加一倍)。 std::array
的大小是固定的,而 a std::array
甚至不管理它指向的块的内存,它仅指向内存的块,了解内存块的长度,了解内存中C数组中的数据类型,并提供方便的访问器功能来处理该连续内存中的元素。
span
是C ++ 20起的C ++标准的一部分。您可以在这里阅读其文档:https://en.cppreference.com/w/cpp/container/span。要了解如何在今天的C ++ 11或更高版本的 中使用Google的std::span
,请参见下文。
absl::Span<T>(array, length)
(std::span<T, Extent>
=“序列中的元素数,如果是动态的,则为Extent
”。跨度仅指向内存并使之易于访问,但无法管理!):std::dynamic_extent
(注意,它的大小固定为 std::array<T, N>
!):N
(根据需要自动动态增加尺寸):std::vector<T>
??Google以其“ Abseil”库的形式开源了其内部C ++ 11库。该库旨在提供C ++ 14至C ++ 20以及在C ++ 11及更高版本中可用的其他功能,以便您可以在今天使用明天的功能。他们说:
与C ++标准的兼容性
Google开发了许多抽象,这些抽象与C ++ 14,C ++ 17及更高版本中包含的功能匹配或紧密匹配。使用这些抽象的Abseil版本,即使您的代码尚未准备好在C ++ 11后的世界中生活,您现在也可以访问这些功能。
span
标头和span.h
模板类:https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L153