Почему я не могу создать gsl :: span со списком инициализаторов в скобках

Согласно Основные положения 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 или есть веская причина, чтобы предотвратить эту практику?

5

Решение

Когда вы вызываете векторную версию, список инициализатора используется для создания временного 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);
8

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

Спан не владеет. Не владеет хранилищем. Это замена арифметики указателей, а не класс хранения.

Вам нужно поместить свои данные в класс хранения, а затем, если вы хотите делать умные вещи с помощью арифметики с указателями, вместо этого вы делаете умные вещи с интервалами.

Вы не можете инициализировать диапазон со списком инициализаторов, потому что некуда поместить данные.

1