Согласно Основные положения C ++, Я должен используйте gsl :: span для передачи полуоткрытой последовательности.
Я думаю, это означает, что вместо написания такой функции, как:
void func(const std::vector<int>& data) {
for (auto v : data) std::cout << v << " ";
}
Я бы предпочел:
void func(gsl::span<const int> data) {
for (auto v : data) std::cout << v << " ";
}
Который имеет преимущество в том, что он не предполагает, что вызывающая сторона хранит свои данные в vector
или заставить их построить временный vector
, Они могли бы передать std::array
например.
Но общий вариант использования — передать заключенный в скобки список инициализатора:
func({0,1,2,3})
Это работает для функции, принимающей std::vector
но для функции, принимающей gsl::span
Я получаю сообщение об ошибке:
ошибка C2664: ‘void func (gsl :: span)’: невозможно преобразовать
аргумент 1 из списка инициализаторов в gsl :: span
Это выглядит как gsl::span
имеет шаблонный конструктор рассчитан на любой контейнер.
Это просто чего-то не хватает в реализации Microsoft GSL или есть веская причина, чтобы предотвратить эту практику?
Когда вы вызываете векторную версию, список инициализатора используется для создания временного std::vector
, который затем передается функции по константной ссылке. Это возможно, потому что std::vector
имеет конструктор, который принимает std::initializer_list<T>
в качестве аргумента.
Тем не мение, gsl::span
не имеет такого конструктора и как {1,2,3}
не имеет типа, он также не может быть принят упомянутым шаблонным конструктором (кроме того факта, что std::initializer_list<T>
не будет удовлетворять концепции контейнера в любом случае).
Конечно, одним (безобразным) обходным решением было бы явное создание временного массива:
func(std::array<int,3>{ 0,1,2,3 });
Я не вижу конкретной причины, почему gsl::span
не должен иметь конструктор, который принимает std::initializer_list
, но имейте в виду, что эта библиотека все еще довольно новая и находится в стадии активной разработки. Так что, может быть, это то, что они упустили из виду, у них не было времени для реализации, не были уверены, как это сделать правильно, или есть действительно некоторые детали, которые могли бы сделать эту конструкцию опасной. Вероятно, лучше спросить разработчиков прямо на github.
РЕДАКТИРОВАТЬ:
Как объясняет @Nolol Bolas в своем комментарии, это было по дизайну потому что список инициализатора как {0,1,2,3}
(и элементы внутри) является временным объектом и как gsl::span
это не контейнер сам по себе (он не берет на себя владение элементами), они думают, что было бы слишком легко случайно создать gsl::span
который содержит висячую ссылку на эти временные элементы.
Итак, пока все будет в порядке:
func({ 0,1,2,3 });
поскольку время жизни списка инициализатора заканчивается после завершения функции, что-то вроде этого создаст висячую ссылку:
gsl::span<const int> data{ 0,1,2,3 };
func(data);
Спан не владеет. Не владеет хранилищем. Это замена арифметики указателей, а не класс хранения.
Вам нужно поместить свои данные в класс хранения, а затем, если вы хотите делать умные вещи с помощью арифметики с указателями, вместо этого вы делаете умные вещи с интервалами.
Вы не можете инициализировать диапазон со списком инициализаторов, потому что некуда поместить данные.