Это не работает, потому что это не должно работать. Это дизайн функции. Это список инициализации , который, как следует из названия, предназначен для инициализации чего-либо.
Когда C ++ 11 ввел initializer_list
, это было сделано только с одной целью: разрешить системегенерировать массив значений из фигурного списка инициализации и передавать их правильно помеченному конструктору (возможно, косвенно), чтобы объект мог инициализировать себя с этой последовательностью значений. «Правильный тег» в этом случае заключается в том, что конструктор принял только что выделенный тип std::initializer_list
в качестве первого / единственного параметра. Это и есть цель initializer_list
как типа.
Инициализация, вообще говоря, не должна изменять значения, которые ему даны. Тот факт, что массив, поддерживающий список, является временным, также дублирует идею о том, что входные значения должны быть логически неизменяемыми. Если у вас есть:
std::vector<int> v = {1, 2, 3, 4, 5};
Мы хотим дать компилятору свободу сделать этот массив из 5 элементов статическим массивом в скомпилированном двоичном файле, а не массивом стека, который увеличивает размер стека этой функции. Более того, мы логически хотим думать о фигурном списке инициализации как о литерале.
И мы не разрешаем модифицировать литералы.
Ваша попытка сделать {a, b, c, d}
в диапазон изменяемых ссылок, по сути, пытается взять конструкцию, которая уже существует для одной цели, и превратить ее в конструкцию для другой цели. Вы ничего не инициализируете;вы просто используете, казалось бы, удобный синтаксис, который создает итеративные списки значений. Но этот синтаксис называется «braced- init -list», и он генерирует список initializer ;эта терминология не случайна.
Если вы возьмете инструмент, предназначенный для X, и попытаетесь угнать его, сделав Y, вы, вероятно, встретите неровные края где-нибудь в будущем. Поэтому причина, по которой это не работает, заключается в том, что это не предназначено для ;это не то, для чего предназначены braced-init-lists и initializer_list
s.
Вы могли бы затем сказать "если это так, почему for(auto i: {1, 2, 3, 4, 5})
вообще работает, если только braced-init-listsдля инициализации? "
Когда-то, это не ;в C ++ 11 это было бы неправильно. Только в C ++ 14 auto l = {1, 2, 3, 4};
стал легальным синтаксисом;переменной auto
было разрешено автоматически выводить список фигурных скобок инициализации как initializer_list
.
На основе диапазона for
использует вычет auto
для типа диапазона, поэтому она унаследовала эту способность. Это, естественно, заставило людей поверить, что фигурные скобки-списки инициализации - это создание диапазонов значений, а не инициализация вещей.
Короче говоря, чья-то удобная функция привела вас к мысли, что конструкция, предназначенная для инициализации объектов, являетсябыстрый способ создать массив. Этого никогда не было.
Установив, что braced-init-lists не предполагают , чтобы делать то, что вы от них хотите, что нужно, чтобы заставить их делать то, чтоВы хотите?
Ну, есть два основных способа сделать это: маленький и большой. Крупномасштабная версия должна была бы изменить работу auto i = {a, b, c, d};
, чтобы она могла (на основе чего-либо) создавать изменяемый диапазон ссылок на выражения. Основанный на диапазоне for
просто использовал бы существующую auto
-средуктивную машину, чтобы поймать его.
Это, конечно, не стартер. Это определение уже имеет четко определенное значение: оно создает неизменяемый список копий этих выражений, а не ссылок на их результаты. Изменение было бы серьезным изменением.
Мелкомасштабным изменением было бы взломать основанный на диапазоне for
, чтобы сделать какой-то причудливый вывод, основанный на том, является ли выражение диапазона скобочным списком инициализации или нет. Теперь, поскольку такие диапазоны и их итераторы скрыты сгенерированным компилятором кодом для основанного на диапазонах for
, у вас не будет столько проблем обратной совместимости. Таким образом, вы могли бы создать правило, в котором, если ваш оператор диапазона определяет не-const
ссылочную переменную, а выражение диапазона является фигурным списком инициализации, тогда у вас есть несколько различных механизмов вывода.
Самая большая проблема здесь в том, что это полный взлом. Если это полезно сделать for(auto &i : {a, b, c d})
, то, вероятно, полезно иметь возможность получить такой же диапазон вне цикла for
на основе диапазона. В настоящее время правила о auto
выводе фигурных скобок init-списков повсеместны. Основанный на диапазоне for
получает свои возможности просто потому, что использует auto
вычет.
Последнее, что нужно C ++, это больше несоответствий .
Это вдвойне важно вlight of C ++ 20 добавление оператора init в диапазон- for
. Эти две вещи должны быть эквивалентны:
for(auto &i : {a, b, c, d})
for(auto &&rng = {a, b, c, d}; auto &i: rng)
Но если вы измените правила, основываясь только на выражении диапазона и выражении диапазона, они не будут. rng
будет выведено в соответствии с существующими правилами auto
, что сделает auto &i
неработоспособным. И наличие правила супер-специального случая, которое меняет поведение оператора init диапазона for
, отличного от того же оператора в других местах, было бы еще более противоречивым.
Кроме того, оно неслишком сложно написать обобщенную функцию reference_range
, которая принимает не const
переменные ссылочные параметры (того же типа) и возвращает некоторый диапазон произвольного доступа по ним. Это будет работать везде одинаково и без проблем с совместимостью.
Так что давайте просто не будем пытаться превратить синтаксическую конструкцию, предназначенную для инициализации объектов, в общий инструмент «list of stuff».