Чи гарантовано виклик деструктора локального об'єкта всередині циклу до наступної ітерації?


11

Коли у мене є цикл і всередині цього циклу створюю нову змінну стека (не виділяючи її на купу і змінну, утримуючи її, оголошену всередині корпусу циклу), чи гарантовано викликати руйнування цього об'єкта до початку наступної ітерації, або може розгортання циклу компілятором щось змінить?


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 Де визначено, що ? Зауважте, що це обговорення є поза темою. Ви можете задати ще одне окреме запитання щодо цієї проблеми.
Даніель Лангр

8

Так. Це легше візуалізувати, якщо врахувати "блоки", в яких ви оголошуєте змінну, тобто між якою парою дужок. Цикл сам по собі є блоком, і коли він досягне дужки закриття, перед наступною ітерацією викликаються всі деструктори змінних автоматичного зберігання, оголошені в циклі.

може, розгортання циклу компілятором щось змінить?

Як правило, не думайте про те, що компілятор буде оптимізувати, тому що він все ще повинен гарантувати поведінку вашої програми, незалежно від того, що вона робить для її оптимізації. У цьому випадку розгортання циклу нічого не змінить до цього ефекту, якщо це станеться.


2
+1 для великого правила, коли пишуть код, не слід турбуватися про внутрішній компілятор.
Збирався

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 перепризначається новим значенням?
користувач1282931

Я теж не на 100% впевнений. Виправте мою відповідь, якщо ви знайшли щось не так. :)
Джуліан Шнабель


0

Звичайно, викликається dtor в кінці ітерації, і розгортання циклу не повинно змінювати цю поведінку, як будь-яка інша оптимізація (оптимізація не повинна змінювати поведінку програми), за винятком якоїсь RVO і подібної, яка може усунути деякі семантично хибні створення об'єктів .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.