小编典典

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

all

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

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


阅读 91

收藏
2022-04-04

共1个答案

小编典典

它是什么?

一个span<T>是:

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

它以前称为
anarray_view甚至更早于array_ref.

我应该什么时候使用它?

一、什么时候 使用:

  • 不要在可能只使用任何一对开始和结束迭代器的代码中使用它,例如,std::sort和所有这些超通用模板化函数。std::find_if``std::copy
  • 如果您有一个您知道适合您的代码的标准库容器(或 Boost 容器等),请不要使用它。它并不打算取代其中任何一个。

现在了解何时实际使用它:

当分配的长度或大小也很重要时,使用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.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

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

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

但它在标准库中吗?

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

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

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

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

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

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

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

2022-04-04