Ответ 1
Что это?
A span<T>
это:
- Очень легкая абстракция непрерывной последовательности значений типа T где-то в памяти.
- В основном это
struct { T * ptr; size_t length; }
с кучей удобных методов. - Несобственный тип (то есть "ссылочный тип", а не "тип значения"): он никогда ничего не выделяет и не освобождает и не поддерживает работу умных указателей.
Ранее он назывался array_view
, а еще раньше - array_ref
.
Когда я должен использовать это?
Во-первых, когда его не использовать:
- Не используйте его в коде, который может занять любую пару start & конечные итераторы, такие как
std::sort
,std::find_if
,std::copy
и все эти супер-общие шаблонные функции. - Не используйте его, если у вас есть стандартный библиотечный контейнер (или контейнер Boost и т.д.), Который, как вы знаете, подходит для вашего кода. Он не предназначен для вытеснения любого из них.
Теперь о том, когда на самом деле использовать его:
Use
span<T>
(respectively,span<const T>
) instead of a free-standingT*
(respectivelyconst 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
...
означает, что вы можете работать с этой комбинацией указатель + длина/начало + конец указателя, как если бы вы работали с причудливым, развернутым стандартным контейнером библиотеки, например:
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
) - означает, что вашему коду (который использует span) не принадлежит указатель.
Существует еще больше мотивов для использования span
, которые вы можете найти в C++ основных принципах - но вы поймете, что это дрейф.
Почему его нет в стандартной библиотеке (по состоянию на C++ 17)?
Он находится в стандартной библиотеке - но только с C++ 20. Причина в том, что он все еще довольно новый в своей нынешней форме, задуманный в связи с проектом C++ основных рекомендаций, который формируется только с 2015 года. (Хотя, как отмечают комментаторы, ранее история.)
Итак, как мне его использовать, если его нет в стандартной библиотеке?
Он является частью библиотеки основных принципов поддержки (GSL). Реализации:
- Microsoft/Neil Macintosh GSL содержит отдельную реализацию:
gsl/span
- GSL-Lite - реализация всего GSL с одним заголовком (он не такой большой, не волнуйтесь), включая
span<T>
.
Реализация GSL обычно предполагает платформу, которая реализует поддержку C++ 14 [14]. Эти альтернативные реализации с одним заголовком не зависят от возможностей GSL:
martinmoene/span-lite
требуется C++ 98 или более поздняя версияtcbrindle/span
требуется C++ 11 или более поздняя версия
Обратите внимание, что вы можете использовать его с более ранними версиями языкового стандарта - C++ 11 и C++ 14, а не только с C++ 17.
Дальнейшее чтение: Вы можете найти все детали и конструктивные соображения в окончательном официальном предложении до C++ 17, P0122R7: span: безопасные для границ представления для последовательностей объектов Нила Макинтоша и Стефана J Лававей. Это немного долго, хотя. Кроме того, в C++ 20 изменилась семантика сравнения промежутков (после этой короткой статьи Тони ван Эрда).