什么是“跨度”,我应该什么时候使用一个?

最近我得到了一些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::sortstd::find_ifstd::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开始)?

  1. 它目前的forms还很新,与C ++核心指南项目一起构思,该项目自2015年以来才刚刚起步。(虽然正如评论者所指出的那样,它已经有了较早的历史)。
  2. 它可能很快成为标准库的一部分(C ++ 20)。 请参阅2017年6月Neil Macintosh 的提案 。

那么如果它不在标准库中,我该如何使用它呢?

它是核心指南的支持图书馆(GSL)的一部分。 实现:

  • Microsoft / Neil Macintosh的GSL包含一个独立的实现: gsl/span
  • GSL-Lite是整个GSL的单个文件实现(它不是那么大,不用担心),包括span<T>