什么是“跨度”,我应该什么时候使用一个?
最近我得到了一些build议,在我的代码中使用span<T>
,或者在网站上看到了一些使用span
的东西 – 据说是某种容器。 但是 – 在C ++标准库中找不到类似的东西。
那么这个神秘的span<T>
是什么?如果它不是标准的,为什么(或何时)使用它是一个好主意?
它是什么?
span<T>
是:
- 内存中某处T型值的连续序列的一个非常轻量级的抽象。
- 基本上是一个
struct { T * const ptr; size_t length; }
struct { T * const ptr; size_t length; }
struct { T * const ptr; size_t length; }
一堆便利的方法。 - 非拥有types(即“引用types”而不是“值types”):它从不分配也不分配任何东西,并且不保留智能指针活着。
它以前被称为array_view
,甚至更早地被称为array_ref
。
我应该什么时候使用它?
首先,当不使用它时:
- 不要在代码中使用它,可以采用任何一对开始和结束迭代器,如
std::sort
,std::find_if
,std::copy
和所有这些超级通用模板化函数。 - 如果你有一个标准的库容器(或者一个Boost容器等),那么不要使用它,你知道它是适合你的代码的。 它并不是要取代它们中的任何一个。
现在什么时候才能真正使用它:
使用
span<T>
(分别是span<const T>
)而不是一个有长度值的独立T*
(分别是const T*
)。 所以,replacefunction如下:void read_into(int* buffer, int 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);
…这将做你想做的事情。 另见准则P.5 。
-
是当你期望你的数据在内存中是连续的时候传递
const vector<T>&
的合理替代方法。 没有更多的高级和强大的C ++大师的谴责。 -
有利于静态分析,所以编译器可能能够帮助你捕捉到愚蠢的错误。
- 允许进行运行时边界检查的debugging编译工具(例如,
span
的方法将在#ifndef NDEBUG
…#endif
有一些边界检查代码) - 表示您的代码(即使用span)不拥有指针。
使用span
的动机更多,你可以在C ++核心指南中find – 但是你可以发现漂移。
为什么它不在标准库(从C ++ 17开始)?
- 它目前的forms还很新,与C ++核心指南项目一起构思,该项目自2015年以来才刚刚起步。(虽然正如评论者所指出的那样,它已经有了较早的历史)。
- 它可能很快成为标准库的一部分(C ++ 20)。 请参阅2017年6月Neil Macintosh 的提案 。
那么如果它不在标准库中,我该如何使用它呢?
它是核心指南的支持图书馆(GSL)的一部分。 实现:
- Microsoft / Neil Macintosh的GSL包含一个独立的实现:
gsl/span
- GSL-Lite是整个GSL的单个文件实现(它不是那么大,不用担心),包括
span<T>
。