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

新手上路,请多包涵

最近我收到了在我的代码中使用 span<T> 的建议,或者在网站上看到了一些使用 span 的答案 - 据说是某种容器。但是 - 我在 C++17 标准库中找不到类似的东西。

那么这个神秘的 span<T> 是什么,如果它是非标准的,为什么(或何时)使用它是个好主意?

原文由 einpoklum 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 638
1 个回答

它是什么?

span<T> 是:

  • 内存中某处 T 类型的连续值序列的非常轻量级的抽象。
  • 基本上是一个 struct { T * ptr; std::size_t length; } 有一堆方便的方法。
  • 非拥有类型(即 “引用类型” 而不是“值类型”):它从不分配或取消分配任何东西,也不使智能指针保持活动状态。

它以前称为 array_view 甚至更早为 array_ref

我应该什么时候使用它?

首先,什么时候 使用跨度:

  • 不要在代码中使用可能只采用任何一对开始和结束迭代器的跨度(例如 std::sortstd::find_ifstd::copy 和其他模板函数 <algorithm> ),并且不在采用任意范围的代码中(有关这些 信息,请参阅 C++20 范围库)。跨度比一对迭代器或范围有更严格的要求:元素连续性和元素在内存中的存在。
  • 如果您有一个您知道适合您的代码的标准库容器(或 Boost 容器等),请不要使用跨度。 span 并非旨在取代现有的容器。

现在了解何时实际使用跨度:

Use span<T> (respectively, span<const T> ) instead of a free-standing T* (respectively const T* ) when the allocated length or size also matter .因此,替换如下功能:

>  void read_into(int* buffer, size_t buffer_size);
>
> ```
>
> 和:
>
> ```
>  void read_into(span<int> buffer);
>
> ```

## 我为什么要使用它?为什么这是一件好事?

哦,跨度太棒了!使用跨度...

- 意味着您可以使用指针+长度/开始+结束指针组合,就像使用花哨的、拉皮条的标准库容器一样,例如:


  - `for (auto& x : my_span) { /* do stuff */ }`
  - `std::find_if(my_span.cbegin(), my_span.cend(), some_predicate);`
  - `std::ranges::find_if(my_span, some_predicate);` (在 C++20 中)


 ...但绝对没有大多数容器类产生的开销。

- 有时让编译器为你做更多的工作。例如,这个:


int buffer[BUFFER_SIZE]; read_into(buffer, BUFFER_SIZE);



  变成这样:


int buffer[BUFFER_SIZE]; read_into(buffer);

”`

…这将做你想做的事。另见 指南 P.5

  • 当您希望数据在内存中连续时,将 const vector<T>& 传递给函数是合理的替代方法。再也不用被高大上的 C++ 大师骂了!

  • 有助于静态分析,因此编译器可能能够帮助您捕获愚蠢的错误。

  • 允许用于运行时边界检查的调试编译工具(即 span 的方法将在 #ifndef NDEBUG#endif 中有一些边界检查代码)

  • 表示您的代码(使用跨度)不拥有指向的内存。

使用 span s 的动机甚至更多,您可以在 C++ 核心指南 中找到它 - 但您会发现偏差。

但它在标准库中吗?

编辑: 是的, std::span 是使用 C++20 版本的语言添加到 C++ 中的!

为什么只在 C++20 中?好吧,虽然这个想法并不新鲜——它目前的形式是与 C++ 核心指南 项目一起构思的,该项目在 2015 年才开始形成。所以花了一段时间。

那么如果我正在编写 C++17 或更早版本,我该如何使用它呢?

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

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

GSL 实现通常假设一个实现 C++14 支持的平台 [ 12 ]。这些替代的单头实现不依赖于 GSL 设施:

请注意,这些不同的 span 实现在它们附带的方法/支持功能方面存在一些差异;它们也可能与 C++20 标准库中采用的版本有所不同。


进一步阅读: 您可以在 C++17、P0122R7 之前的最终官方提案中找到所有细节和设计注意事项: 跨度:Neal Macintosh 和 Stephan J. Lavavej 的对象序列的边界安全视图。虽然有点长。此外,在 C++20 中,跨度比较语义发生了变化(在 Tony van Eerd 的 这篇简短论文 之后)。

原文由 einpoklum 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题