Гарантируется ли деструктор локального объекта внутри цикла перед следующей итерацией?

11

Когда у меня есть цикл, и внутри этого цикла создается новая переменная стека (без выделения ее в куче и переменной, содержащей ее, объявленной внутри тела цикла), гарантированно вызывается деструктор этого объекта перед началом следующей итерации, или может Развертывание цикла компилятором что-то изменит?

user1282931
источник
1
Развертывание цикла само по себе не изменит порядок выполнения. Тем не менее, параллелизация цикла может сделать это.
Адриан Моул

Ответы:

8

От n4800:

§6.3.3 Объем блока :

Имя, объявленное в блоке (8.3), является локальным для этого блока; у него есть область видимости блока. Его потенциальная область действия начинается в точке объявления (6.3.2) и заканчивается в конце его блока. Переменная, объявленная в области видимости блока, является локальной переменной.

§10.3.6 Деструкторы :

Деструктор вызывается неявно [...] при выходе из блока, в котором создан объект (8.7)

§4.1.1 Абстрактная машина :

Это положение иногда называют правилом «как будто», потому что реализация может свободно игнорировать любое требование этого документа, если в результате будет выполнено требование, насколько это может быть определено из наблюдаемого поведения программа .

[Акцент мой]

Так да. Ваша переменная выходит из области видимости в конце цикла (который является блоком), и, следовательно, ее деструктор вызывается настолько, насколько каждый может наблюдать за поведением программы .

Пол Эванс
источник
1
Там нет ничего о КОГДА деструктор называется.
абсолютное
2
@stark. То, что позволяет им это делать, это правило «как будто». Стандарт только определяет поведение абстрактной машины. Не уверен, стоит ли вдаваться во все эти детали в ответах здесь.
Макс Лангхоф
2
@stark Это, IMO, не имеет отношения к вопросу. С тем же успехом можно сказать, что деструкторы могут быть встроенными, а потому и вовсе не редактироваться call. Или, если они эффективно (как если бы правило) ничего не делают, может не быть сборки для таких генерируемых деструкторов.
Даниэль Лангр
2
@stark Посмотрите, что именно является правилом «как будто»? ,
Даниэль Лангр
2
@ Старк Где определяется что ? Обратите внимание, что это обсуждение не по теме. Вы можете задать еще один отдельный вопрос по этой проблеме.
Даниэль
8

Да. Это проще визуализировать, когда вы рассматриваете «блоки», в которых вы объявляете переменную, т.е. между какой парой скобок. Цикл сам по себе является блоком, и, когда он достигает закрывающей скобки, перед следующей итерацией вызываются все деструкторы переменных автоматического хранения, объявленные в цикле.

может развернуть цикл компилятором что-нибудь об этом?

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

JBL
источник
2
+1 для практического правила, при написании кода не стоит беспокоиться о внутренностях компилятора. Собирался добавить что-то в том же духе к моему ответу, но теперь это уже есть
idclev 463035818
copy-elision и RVO изменяют поведение программы, не так ли?
Жан-Батист Юнес
@ Jean-BaptisteYunès Они потенциально могут, однако, стандарт позволяет им слишком[class.copy.elision]
ChrisMM
Не просто пара скобок . Вы можете написать, for(...) X x{};и xобъект будет построен + разрушен в каждой итерации. Живая демоверсия . Соответствующим Стандартным разделом является stmt.iter / 2 .
Даниэль Лангр
@DanielsaysreinstateMonica В соответствии с §9.5.2 [stmt.iter]это чисто эквивалентно (выделено мое): «Если подстановка в выражении итерации является одним оператором, а не составным оператором, это как если бы оно было переписано, чтобы быть составным оператором, содержащим оригинальное утверждение . По сути, с или без скобок для одного утверждения означает абсолютно то же самое, и скобки являются неявными. Я опустил это для ясности.
JBL
2

Деструктор вызывается для каждой итерации. Таким образом, в некоторых случаях быстрее объявить переменную вне цикла, а не в цикле. Предполагая следующий случай:

std::string temp;
for(int i = 0; i < 10; ++i){
    temp = arr[i];
    doSomething(temp);
}

Деструктор не вызывается при использовании цикла. Это просто отменяет temp.

Но если вы используете std::string temp = arr[i]конструктор и деструктор вызывается для каждой итерации. Я думаю, что это добавляет немного времени выполнения, если у вас есть цикл, который выполняется очень часто.

Джулиан Шнабель
источник
Обратите внимание, что вызывать деструкторов или нет - это не просто вопрос производительности. Когда у вас есть тип RAII, вы специально хотите, чтобы деструктор вызывался на каждой итерации
idclev 463035818
не уверен, что это правда. Разве деструктор содержимого 'temp', удерживаемый из предыдущей итерации, не вызывается правильно, когда temp переназначается с новым значением?
user1282931
Я не уверен на 100%. Исправьте мой ответ, если вы нашли что-то не так. :)
Джулиан Шнабель
0

Деструктор вызывается перед следующей итерацией

Girspoon
источник
0

Конечно, dtor вызывается в конце итерации, и развертывание цикла не должно изменять это поведение, как любая другая оптимизация (оптимизация не должна изменять поведение программы), за исключением некоторого вида RVO и т. П., Которые могут устранить некоторые семантически ложные создания объектов ,

Жан-Батист Юнес
источник