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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45723819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:40:09  来源:igfitidea点击:

What is a "span" and when should I use one?

c++c++20c++-faqcpp-core-guidelinesstd-span

提问by einpoklum

Recently I've gotten suggestions to use span<T>'s in my code, or have seen some answers here on the site which use span's - supposedly some kind of container. But - I can't find anything like that in the C++17 standard library.

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

So what is this mysterious span<T>, and why (or when) is it a good idea to use it if it's non-standard?

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

回答by einpoklum

What is it?

它是什么?

A span<T>is:

一个span<T>是:

  • A very lightweight abstraction of a contiguous sequence of values of type Tsomewhere in memory.
  • Basically a struct { T * ptr; std::size_t length; }with a bunch of convenience methods.
  • A non-owning type (i.e. a "reference-type"rather than a "value type"): It never allocates nor deallocates anything and does not keep smart pointers alive.
  • T内存中某处类型的连续值序列的非常轻量级的抽象。
  • 基本上是struct { T * ptr; std::size_t length; }一堆方便的方法。
  • 非拥有类型(即“引用类型”而不是“值类型”):它从不分配或解除分配任何东西,也不使智能指针保持活动状态。

It was formerly known as an array_viewand even earlier as array_ref.

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

When should I use it?

我应该什么时候使用它?

First, when notto use it:

首先,什么时候使用它:

  • Don't use it in code that could just take any pair of start & end iterators, like std::sort, std::find_if, std::copyand all of those super-generic templated functions.
  • Don't use it if you have a standard library container (or a Boost container etc.) which you know is the right fit for your code. It's not intended to supplant any of them.
  • 不要在可能只采用任何一对开始和结束迭代器的代码中使用它,比如std::sort, std::find_ifstd::copy以及所有这些超通用模板化函数。
  • 如果您有一个标准库容器(或 Boost 容器等),并且您知道它适合您的代码,请不要使用它。它无意取代其中任何一个。

Now for when to actually use it:

现在是什么时候真正使用它:

Use span<T>(respectively, span<const T>) instead of a free-standing T*(respectively const T*) for which you have the length value. So, replace functions like:

  void read_into(int* buffer, size_t buffer_size);

with:

  void read_into(span<int> buffer);

使用span<T>(分别, span<const T>) 而不是具有长度值的独立式T*(分别const T*)。因此,替换如下函数:

  void read_into(int* buffer, size_t buffer_size);

和:

  void read_into(span<int> buffer);

Why should I use it? Why is it a good thing?

我为什么要使用它?为什么是好事?

Oh, spans are awesome! Using a span...

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

  • means that you can work with that pointer+length / start+end pointer combination like you would with a fancy, pimped-out standard library container, e.g.:

    • for (auto& x : my_span) { /* do stuff */ }
    • std::find_if(my_span.begin(), my_span.end(), some_predicate);

    ... but with absolutely none of the overhead most container classes incur.

  • lets the compiler do more work for you sometimes. For example, this:

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

    becomes this:

    int buffer[BUFFER_SIZE];
    read_into(buffer);
    

    ... which will do what you would want it to do. See also Guideline P.5.

  • is the reasonable alternative to passing const vector<T>&to functions when you expect your data to be contiguous in memory. No more getting scolded by high-and-mighty C++ gurus!

  • facilitates static analysis, so the compiler might be able to help you catch silly bugs.
  • allows for debug-compilation instrumentation for runtime bounds-checking (i.e. span's methods will have some bounds-checking code within #ifndef NDEBUG... #endif)
  • indicates that your code (that's using the span) doesn't own the pointed-to memory.
  • 意味着您可以使用该指针+长度/开始+结束指针组合,就像使用花哨的、拉皮条的标准库容器一样,例如:

    • 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++大师骂了!

  • 便于静态分析,因此编译器可能能够帮助您捕获愚蠢的错误。
  • 允许用于运行时边界检查的调试编译工具(即span的方法将在#ifndef NDEBUG... 中包含一些边界检查代码#endif
  • 表示您的代码(即使用跨度)不拥有指向的内存。

There's even more motivation for using spans, which you could find in the C++ core guidelines- but you catch the drift.

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

Why is it not in the standard library (as of C++17)?

为什么它不在标准库中(从 C++17 开始)?

It is in the standard library - but only as of C++20. The reason is that it's still pretty new in its current form, conceived in conjunction with the C++ core guidelinesproject, which has only been taking shape since 2015. (Although as commenters point out, it has earlier history.)

它在标准库中 - 但仅从 C++20 开始。原因是它目前的形式仍然很新,是与C++ 核心指南项目一起构思的,该项目自 2015 年以来才形成。(尽管正如评论者指出的那样,它有更早的历史。)

So how do I use it if it's not in the standard library yet?

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

It's part of the Core Guidelines's Support Library (GSL). Implementations:

它是Core Guidelines的支持库 (GSL) 的一部分。实现:

  • Microsoft / Neil Macintosh's GSLcontains a standalone implementation: gsl/span
  • GSL-Liteis a single-header implementation of the whole GSL (it's not that big, don't worry), including span<T>.
  • Microsoft / Neil Macintosh 的GSL包含一个独立的实现:gsl/span
  • GSL-Lite是整个 GSL 的单头实现(它没有那么大,别担心),包括span<T>.

The GSL implementation does generally assume a platform that implements C++14 support [14]. These alternative single-header implementations do not depend on GSL facilities:

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

Note that these different span implementations have some differences in what methods/support functions they come with; and they may also differ somewhat from the version going into the standard libraryin C++20.

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



Further reading:You can find all the details and design considerations in the final official proposal before C++17, P0122R7: span: bounds-safe views for sequences of objectsby Neal Macintosh and Stephan J. Lavavej. It's a bit long though. Also, in C++20, the span comparison semantics changed (following this short paperby Tony van Eerd).

进一步阅读:您可以在 C++17 之前的最终官方提案 P0122R7 中找到所有细节和设计注意事项:跨度:Neal Macintosh 和 Stephan J. Lavavej对对象序列的边界安全视图。不过时间有点长。另外,在C ++ 20,跨度比较语义改变(以下该短纸由Tony面包车EERD)。

回答by Gabriel Staples

@einpoklum does a pretty good job of introducing what a spanis in his answer here. However, even after reading his answer,it is easy for someone new to spans to still have a sequence of stream-of-thought questions which aren't fully answered, such as the following:

@einpoklum在这里很好地介绍了他的回答中的aspan是什么。然而,即使在阅读了他的回答之后,对于一个新来跨越的人来说,仍然很容易有一系列没有完全回答的思路问题,例如:

  1. How is a spandifferent from a C array? Why not just use one of those? It seems like it's just one of those with the size known as well...
  2. Wait, that sounds like a std::array, how is a spandifferent from that?
  3. Oh, that reminds me, isn't a std::vectorlike a std::arraytoo?
  4. I'm so confused. :( What's a span?
  1. aspan与 C 数组有何不同?为什么不只使用其中之一?似乎它只是已知尺寸的其中之一......
  2. 等等,这听起来像 a,那和 astd::array有什么span不同?
  3. 哦,这让我想起了,是不是太std::vector像了std::array
  4. 我很困惑。:( 什么是span

So, here's some additional clarity on that:

所以,这里有一些额外的澄清:

DIRECT QUOTE OF HIS ANSWER--WITH MY ADDITIONS IN BOLD:

直接引用他的回答——加上我粗体的补充

What is it?

A span<T>is:

  • A very lightweight abstraction of a contiguous sequence of values of type Tsomewhere in memory.
  • Basically a singlestruct { T * ptr; std::size_t length; }with a bunch of convenience methods. (Notice this is distinctly different from std::array<>because a spanenables convenience accessor methods, comparable to std::array, via a pointer to type Tand length (number of elements) of type T, whereas std::arrayis an actual container which holds one or more valuesof type T.)
  • A non-owning type (i.e. a "reference-type"rather than a "value type"): It never allocates nor deallocates anything and does not keep smart pointers alive.

It was formerly known as an array_viewand even earlier as array_ref.

它是什么?

一个span<T>是:

  • T内存中某处类型的连续值序列的非常轻量级的抽象。
  • 基本上是一个单一的结构{ T * ptr; std::size_t length; }与一群的方便的方法。(请注意,这明显不同于std::array<>因为 aspan启用了方便的访问器方法,类似于std::array,通过指向 type 的类型T和长度(元素数量)的指针T,而std::array是一个实际容器,其中包含一个或多个typeT。)
  • 非拥有类型(即“引用类型”而不是“值类型”):它从不分配或解除分配任何东西,也不使智能指针保持活动状态。

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

Those bold parts are criticalto one's understanding, so don't miss them or misread them! A spanis NOT a C-array of structs, nor is it a struct of a C-array of type Tplus the length of the array (this would be essentially what the std::arraycontaineris), NOR is it a C-array of structs of pointers to type Tplus the length, but rather it is a singlestruct containing one single pointer to type T, and the length, which is the number of elements (of type T) in the contiguous memory block that the pointer to type Tpoints to!In this way, the only overhead you've added by using a spanare the variables to store the pointer and length, and any convenience accessor functions you use which the spanprovides.

那些粗体部分对一个人的理解至关重要,所以不要错过它们或误读它们!Aspan不是结构的 C 数组,也不是类型T加数组长度的C 数组的结构(这基本上就是std::array容器的内容),也不是指针结构的 C 数组to typeT加上长度,而是一个包含一个指向 type 的指针单个结构体,以及length,它是指向 type 的指针所指向的连续内存块中(类型的元素数量这样,您使用的唯一开销TTTspan是存储指针和长度的变量,以及您使用的任何便利访问器函数span

This is UNLIKE a std::array<>because the std::array<>actually allocates memory for the entire contiguous block, and it is UNLIKE std::vector<>because a std::vectoris basically just a std::arraythat also does dynamic growing(usually doubling in size) each time it fills up and you try to add something else to it. A std::arrayis fixed in size, and a spandoesn't even manage the memory of the block it points to, it just points to the block of memory, knows how long the block of memory is, knows what data type is in a C-array in the memory, and provides convenience accessor functions to work with the elements in that contiguous memory.

这是 UNLIKE astd::array<>因为std::array<>实际上为整个连续块分配内存,它是 UNLIKEstd::vector<>因为 astd::vector基本上只是 a每次它填满时std::array也会动态增长(通常大小加倍)并且您尝试向其中添加其他内容. A的std::array大小是固定的,aspan甚至不管理它指向的块的内存,它只是指向内存块,知道内存块有多长,知道C数组中的数据类型在内存中,并提供方便的访问器函数来处理该连续内存中的元素

It ispart of the C++ standard:

C++ 标准的一部分:

std::spanis part of the C++ standard as of C++20. You can read its documentation here: https://en.cppreference.com/w/cpp/container/span. To see how to use Google's absl::Span<T>(array, length)in C++11 or later today, see below.

std::span自 C++20 起,是 C++ 标准的一部分。您可以在此处阅读其文档:https: //en.cppreference.com/w/cpp/container/span。要了解如何使用谷歌的absl::Span<T>(array, length)在C ++ 11或更高版本的今天,见下文。

Summary Descriptions, and Key References:

摘要说明和主要参考资料:

  1. std::span<T, Extent>(Extent= "the number of elements in the sequence, or std::dynamic_extentif dynamic". A span just points tomemory and makes it easy to access, but does NOT manage it!):
    1. https://en.cppreference.com/w/cpp/container/span
  2. std::array<T, N>(notice it has a fixedsize N!):
    1. https://en.cppreference.com/w/cpp/container/array
    2. http://www.cplusplus.com/reference/array/array/
  3. std::vector<T>(automatically dynamically grows in size as necessary):
    1. https://en.cppreference.com/w/cpp/container/vector
    2. http://www.cplusplus.com/reference/vector/vector/
  1. std::span<T, Extent>Extent=“序列中元素的数量,或者std::dynamic_extent如果是动态的”。跨度只是指向内存并使其易于访问,但不管理它!):
    1. https://en.cppreference.com/w/cpp/container/span
  2. std::array<T, N>(注意它有一个固定的大小N!):
    1. https://en.cppreference.com/w/cpp/container/array
    2. http://www.cplusplus.com/reference/array/array/
  3. std::vector<T>(根据需要自动动态增加大小):
    1. https://en.cppreference.com/w/cpp/container/vector
    2. http://www.cplusplus.com/reference/vector/vector/

How Can I Use spanin C++11 or later today?

今天如何span在 C++11 或更高版本中使用

Google has open-sourced their internal C++11 libraries in the form of their "Abseil" library. This library is intended to provide C++14 to C++20 and beyond features which work in C++11 and later, so that you can use tomorrow's features, today. They say:

谷歌已经以其“Abseil”库的形式开源了他们的内部 C++11 库。该库旨在提供 C++14 到 C++20 以及适用于 C++11 及更高版本的功能,以便您可以在今天使用明天的功能。他们说:

Compatibility with the C++ Standard

Google has developed many abstractions that either match or closely match features incorporated into C++14, C++17, and beyond. Using the Abseil versions of these abstractions allows you to access these features now, even if your code is not yet ready for life in a post C++11 world.

与 C++ 标准的兼容性

Google 开发了许多与 C++14、C++17 及更高版本中包含的特性匹配或紧密匹配的抽象。使用这些抽象的 Abseil 版本允许您现在访问这些功能,即使您的代码还没有准备好在后 C++11 世界中使用。

Here are some key resources and links:

以下是一些关键资源和链接:

  1. Main site: https://abseil.io/
  2. https://abseil.io/docs/cpp/
  3. GitHub repository: https://github.com/abseil/abseil-cpp
  4. span.hheader, and absl::Span<T>(array, length)template class: https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189
  1. 主站:https: //abseil.io/
  2. https://abseil.io/docs/cpp/
  3. GitHub 存储库:https: //github.com/abseil/abseil-cpp
  4. span.h标题和absl::Span<T>(array, length)模板类:https: //github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189