Что составляет неудачную инициализацию статических переменных или переменных продолжительности хранения потока в области блока?

Ответив на этот вопрос и не найдя удовлетворительного ответа в стандартной статье, я начал задаваться вопросом. В стандарте указано следующее: инициализация указанных переменных:

§6.7 [stmt.dcl] p4

[...] В противном случае такая переменная инициализируется при первом прохождении управления через ее объявление; такая переменная считается инициализированной по завершении ее инициализации. Если инициализация завершается выдачей исключения, инициализация не завершена, поэтому она будет повторена, когда в следующий раз элемент управления войдет в объявление.

Нет упоминания о том, что может вызвать повторную попытку инициализации, если она не удалась из-за чего-либо другого, кроме выброса исключения (longjmp(), выход по ссылке, и некоторые другие сигналы).

Я что-нибудь пропустил в стандарте? Я снова и снова просматривал пункты инициализации, объявления и исключения и даже обращался к CWG обнаруживает дефекты в содержании, выполняя быстрый поиск по запросу" статический ", но не может найти ничего связанного.

Является ли это недостаточной спецификацией (и как таковой дефект) в стандарте?


person Xeo    schedule 30.01.2012    source источник
comment
Определяет ли C ++ что-нибудь, относящееся к сигналам? Или longjmp? Выход из потока может что-то изменить.   -  person Lightness Races in Orbit    schedule 30.01.2012
comment
Есть ли у вас в голове какой-либо конкретный сценарий, который вы не находите должным образом определенным стандартом?   -  person Kerrek SB    schedule 30.01.2012
comment
@Kerrek: Связанный вопрос. :П   -  person Xeo    schedule 30.01.2012
comment
@ Светлота: 18.10 / 4 для _1 _ / _ 2_. См. Также это: open-std.org/jtc1/ sc22 / wg21 / docs / cwg_defects.html # 28   -  person Xeo    schedule 30.01.2012
comment
@Xeo: Хм ... Я не совсем понимаю, что двусмысленно в этом вопросе. Не могли бы вы пояснить, почему вы не думаете, что поведение достаточно хорошо определено?   -  person Kerrek SB    schedule 30.01.2012
comment
@Kerrek: Просто не указано, будет ли инициализация повторена, если она не удалась из-за чего-либо еще, кроме исключения.   -  person Xeo    schedule 30.01.2012
comment
@Xeo: А как еще это может потерпеть неудачу? То, что вы считаете проблемой, не имеет отношения к static переменным. Вы можете задать то же самое для любого другого объекта: как узнать, инициализирован ли объект, если не генерируется исключение?   -  person Kerrek SB    schedule 30.01.2012
comment
@Kerrek: Как я уже сказал в вопросе, longjmp, сигналы, ExitThread среди прочего. Кроме того, это характерно для _3 _ / _ 4_ локальных переменных, поскольку они будут полностью инициализированы только один раз. Другие объекты всегда будут инициализироваться снова и снова (поскольку они всегда уничтожаются).   -  person Xeo    schedule 30.01.2012
comment
@Xeo: Но если вы longjmp или ExitThread вне любой конструкции объекта, у вас проблемы, не так ли? Разве это не всегда УБ?   -  person Kerrek SB    schedule 30.01.2012
comment
@Xeo: Когда вы перечисляете другие способы выхода функции, что вы подразумеваете под сигналами? Это не входит в спецификацию C ++.   -  person Nicol Bolas    schedule 30.01.2012
comment
@Kerrek: У тебя будут проблемы с longjmp, да. См. Абзац, который я упомянул в ответ на «Легкость». Однако, как уже было сказано, автоматические объекты будут инициализироваться снова каждый раз при передаче их объявления, чего нельзя сказать о статических и локальных для потока (которые, кстати, являются неявно статическими) локальными объектами. Я просто хочу знать, когда построение будет повторено, в основном, чтобы дать адекватный ответ на связанный вопрос. :)   -  person Xeo    schedule 30.01.2012
comment
@Nicol: C ++ унаследовал сигналы от C, поэтому косвенно является частью его спецификации.   -  person Xeo    schedule 30.01.2012


Ответы (2)


Спецификация C ++ может определять только те вещи, которые содержатся в спецификации C ++. Помните: спецификация C ++ определяет поведение виртуальной машины, которую она определяет. И если он не определяет, что что-то может случиться, он, конечно же, не определяет поведение C ++ в отношении того, что он не говорит, что может произойти.

Согласно спецификации C ++, поток может выйти ровно тремя способами: путем возврата из своей основной функции, выброса исключения через свою основную функцию и прямого выхода из процесса (как с std::terminate или аналогичными функциями). Короче говоря, поток C ++ не может выйти другим способом. В стандартном C ++ нет функции ExitThread. Точно так же std::thread не может убить поток ни снаружи, ни внутри.

Следовательно, все, что действительно вызывает то, что, по словам C ++, не может произойти, по определению не определено. Я предполагаю, что это даже не было бы «неопределенным поведением»; это было бы в том туманном пространстве, в котором были потоки до того, как C ++ 11 фактически изложил, как работают взаимодействия потоков.

То же самое и с «сигналами», какими бы они ни были. В спецификации C ++ не сказано, что они могут вызвать завершение функции. Вот драконы.

Что касается longjmp, это покрывается поведением longjmp. Когда вы используете longjmp для выхода из функции, эта функция никогда не завершается, как если бы вы использовали throw и catch. А в C ++ объект создается только после завершения его конструктора. Следовательно, инициализация объекта никогда не была завершена, и он не инициализирован.

У меня нет спецификации ISO C (на которую ссылается C ++ поведение longjmp), но C ++ 11 настоятельно рекомендует приравнять _10 _ / _ 11_ к _12 _ / _ 13_, если вы получаете неопределенное поведение:

§18.10 [support.runtime] p4:

Сигнатура функции longjmp (jmp_buf jbuf, int val) имеет более ограниченное поведение в этом международном стандарте. Пара вызовов setjmp / longjmp имеет неопределенное поведение, если замена setjmp и longjmp на catch и throw вызовет любые нетривиальные деструкторы для любых автоматических объектов.

Так что я не думаю, что это недооценено. Возможно, он не будет красиво и аккуратно выложен, но все части есть.

person Nicol Bolas    schedule 30.01.2012

Тот факт, что в тексте упоминается один конкретный случай, не означает упущения, что другие будут другими. Если есть другие способы предотвратить завершение инициализации, реализация должна повторить попытку при следующем выполнении.

Я думаю, что ответ Николая в основном правильный, но нетривиальный конструктор не подразумевает нетривиальный деструктор. longjmp может поэтому прервать инициализацию, и ее придется повторить. Это сложно только в многопоточной среде, где мьютекс необходим для предотвращения состояния гонки между потоками, соперничающими за то, чтобы первыми выполнить инициализацию. Фантомному объекту мьютекса нужен нетривиальный деструктор, даже если у инициализированного объекта его нет. Вероятный результат - тупик. Вероятно, это хороший материал для ДР.

person Potatoswatter    schedule 30.01.2012
comment
Дело не в том, что в стандарте указан только один случай, он указывает только повтор именно для этого случая. Он не говорит, что если не удалось, повторите попытку в следующий раз. В частности, сказано, что если не удалось бросить, повторить попытку в следующий раз. - person Xeo; 30.01.2012
comment
@Xeo: Вовсе нет. Если инициализация не завершилась, значит, это не инициализация. Повторная попытка выполняется в первый раз. То, что предыдущий текст уже подразумевает, что это обнаруживается языком, инициализация не завершена, поэтому будет предпринята повторная попытка ... - person Potatoswatter; 30.01.2012
comment
Нет, потому что в тексте перед that упоминаются только исключения. Вы не можете просто отсоединить обе части. В любом случае, я думаю, что это хороший кандидат, по крайней мере, для разъяснения. Кстати, по вопросу мьютекса, я думаю, может быть применимо следующее примечание на странице 138: 88) Реализация не должна вводить какой-либо тупик вокруг выполнения инициализатора. - person Xeo; 31.01.2012
comment
@Xeo: Часть этого предложения, которую я пропустил, конечно же, относится к исключениям. В тексте перед that исключения не упоминаются. Поведение, при котором повторяется инициализация, полностью подразумевается: В противном случае такая переменная инициализируется при первом прохождении управления через ее объявление; такая переменная считается инициализированной после завершения ее инициализации. Другими словами, прохождение только части инициализации ничего не значит, а второй раз по-прежнему считается первым. Кроме того, само понятие попытки вводится только при повторной попытке. - person Potatoswatter; 31.01.2012