Почему auto x{3} выводит список initializer_list?

Я люблю auto в С++ 11. Это замечательно. Но у него есть одно несоответствие, которое действительно действует мне на нервы, потому что я все время спотыкаюсь об него:

int i = 3;       // i is an int with value 3
int i = int{3};  // i is an int with value 3
int i(3);        // i is an int with value 3 (possibly narrowing, not in this case)
int i{3};        // i is an int with value 3

auto i = 3;      // i is an int with value 3
auto i = int{3}; // i is an int with value 3
auto i(3);       // i is an int with value 3
auto i{3};       // wtf, i is a std::initializer_list<int>?!

Это странное поведение сбивает с толку новичков и раздражает опытных пользователей — в C++ достаточно мелких несоответствий и краеугольных случаев, которые нужно всегда иметь в виду. Кто-нибудь может объяснить, почему комитет по стандартам решил ввести новый в данном случае?

Я мог бы понять это, если бы объявление переменной типа std::initializer_list было чем-то полезным или делалось часто, но по моему опыту это почти никогда не делается преднамеренно — и в тех редких случаях, когда вы действительно хотели это сделать, любой из

std::initializer_list<int> l{3};
auto l = std::initializer_list<int>{3};
auto l = {3}; // No need to specify the type

будет работать нормально. Так в чем причина особого случая для auto x{i}?


person Tristan Brindle    schedule 01.09.2014    source источник


Ответы (1)


Короче говоря:

  • выражение инициализатора в фигурных скобках {} само по себе не имеет типа
  • auto должен вывести информацию о типе
  • int{3} очевидно означает "создать переменную int со значением, взятым из списка инициализаторов", таким образом, ее тип - просто int и может использоваться в любом более широком контексте (int i = int{3} будет работать, а auto i = int{3} может вывести тип, потому что правая сторона явно относится к типу int)
  • {3} сам по себе не имеет типа (он не может быть int, потому что это не значение, а список инициализаторов), поэтому auto не будет не работает — но, поскольку комитет посчитал, что auto все равно должен работать в этом случае, они решили, что «лучшим» типом для (да, бестипового по определению) списка инициализаторов будет... std::initializer_list, как вы уже, наверное, догадались.

Но, как вы указали, это сделало все поведение auto совершенно семантически непоследовательным. Вот почему в комитет были представлены предложения по его изменению, а именно N3681, N3912 и N3922. Предыдущее предложение было ОТКЛОНЕНО как FI3 из-за отсутствия консенсуса комитета по этому вопросу, http://isocpp.org/files/papers/n3852.html#FI3 , текущий (N3922) получил принято ок. 1 квартал 2015 г.;

tl;dr вы можете предположить, что стандарты совместимые компиляторы1 с современной поддержкой C++2 новая, более разумная семантика либо уже используется, либо скоро появится.

Комитет по стандартизации признал проблему, приняв N3922 в проект C++17.

— so it's

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int

сейчас, к лучшему или к худшему.

дальнейшее чтение:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3681.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html

http://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html

http://herbsutter.com/2014/11/24/updates-to-my-trip-report/


1GCC 5.1 (и выше) очевидно, использует N3922 даже в режиме C++11/C++14

2Clang 3.8, с оговоркой

Это обратно несовместимое изменение применяется ко всем языковым версиям, допускающим вывод типа из auto (по запросу комитета C++).

person Community    schedule 01.09.2014
comment
N3922 был принят на вооружение более года назад (ноябрь 2014 года, в Урбане). - person T.C.; 19.02.2016
comment
@Т.С. Большое спасибо за то, что заметили это - и странно, что никто не упомянул об этом раньше! - person ; 19.02.2016
comment
@vaxquis Ребята, вы знаете, когда это будет принято и применено к большинству компиляторов, таких как g++ и clang? - person Curious; 22.02.2016
comment
@ Любопытно, что просмотр официальной документации по компилятору или использование Google должны ответить на этот вопрос лучше, чем я. Тем не менее, я обновил ответ соответственно. - person ; 22.02.2016
comment
@vaxquis Почему X3 (прямая инициализация) является ошибкой, а X1 (инициализация копией) допустима? - person gedamial; 03.06.2016
comment
@gedamial смотрите связанные статьи для объяснения. обратите внимание, что x1 допустимо, потому что RH явно std::initializer_list<int> - OTOH, прямая инициализация примитива требует одного параметра {value}, поэтому с {value1,value2} это не имеет большого смысла. - person ; 03.06.2016
comment
Как ни странно, текущие GCC и Clang отвергают auto i{1,2}; даже в режиме С++ 11, хотя это допустимо в этой стандартной версии и принимается, например. GCC 4.8 (сведен к std::initializer_list<int>). Я прочитал N3922, в котором говорится, что этот вывод считается дефектом в С++ 14. То есть это означает, что задним числом даже программы, написанные для более старых стандартных версий, считаются плохо сформированными? - person Arne Vogel; 22.01.2018
comment
@ArneVogel Думаю, да. В ответе цитировалось явное предостережение Clang о том, что изменение несовместимо с предыдущими версиями и применимо ко всем языковым версиям. - person Yongwei Wu; 30.04.2018