Что такое «span» и когда я должен использовать один?

Недавно я получил предложения по использованию span<T>в моем коде, или видел некоторые ответы здесь на сайте, которые используют spanх — якобы какой-то контейнер. Но — я не могу найти ничего подобного в стандартной библиотеке C ++.

Так что же это за таинственное span<T>и почему (или когда) лучше использовать его, если он нестандартный?

151

Решение

Что это?

span<T> является:

  • Очень легкая абстракция непрерывной последовательности значений типа T где-то в памяти.
  • В основном struct { T * const ptr; size_t length; } с кучей удобных методов.
  • Не владеющий тип (т.е. «Ссылка типа» а не «тип значения»): он никогда ничего не выделяет и не освобождает и не поддерживает умные указатели.

Ранее он был известен как array_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.begin(), my_span.end(), some_predicate);

    … но без каких-либо накладных расходов большинство контейнерных классов.

  • позволяет компилятору иногда выполнять за вас больше работы. Например, это:

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

    становится так:

    int buffer[BUFFER_SIZE];
    read_into(buffer);
    

    … который будет делать то, что вы хотели бы это сделать. Смотрите также Положение С.5.

  • является разумной альтернативой для прохождения const vector<T>& к функциям, когда вы ожидаете, что ваши данные будут непрерывными в памяти. Больше не надо ругать могучих гуру C ++.

  • облегчает статический анализ, поэтому компилятор может помочь вам обнаружить глупые ошибки.

  • допускает инструментарий отладки-компиляции для проверки границ во время выполнения (т.е. spanметоды будут иметь некоторый код проверки границ внутри #ifndef NDEBUG#endif)
  • указывает, что ваш код (который использует span) не владеет указателем.

Там еще больше мотивации для использования spanс, которые вы могли бы найти в Основные принципы C ++ — но вы ловите дрейф

Почему его нет в стандартной библиотеке (по состоянию на C ++ 17)?

Он есть в стандартной библиотеке — но только на C ++ 20. Причина в том, что он все еще довольно новый в его нынешнем виде, задуманный в сочетании с Основные принципы C ++ Проект, который только формируется с 2015 года. (Хотя, как отмечают комментаторы, он имеет более раннюю историю.)

Так как мне его использовать, если его еще нет в стандартной библиотеке?

Это часть Основные руководящие принципыБиблиотека поддержки (GSL). Реализации:

  • Microsoft / Нил Макинтош GSL содержит отдельную реализацию: gsl/span
  • GSL-Lite это однофайловая реализация всего GSL (она не такая большая, не волнуйтесь), включая span<T>,

Обратите внимание, что вы можете использовать его с более ранними версиями стандарта языка — C ++ 11 и C ++ 14, а не только с C ++ 17.


Дальнейшее чтение: Вы можете найти все детали и конструктивные соображения в окончательном официальном предложении до C ++ 17, P0122R7: span: безопасные границы для последовательностей объектов Нил Макинтош и Стефан Дж. Лававей. Это немного долго, хотя. Также в C ++ 20 изменилась семантика сравнения диапазонов эта короткая статья Тони ван Эрд).

171

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]