Подтвердить что ты не робот

Время жизни неназванного временного построенного в выражении условия if

Как стандарт определяет время жизни временного объекта, построенного при оценке выражения условия if?

Я искал эту информацию и нашел что-то подобное в примере, чтобы указать [10] в $1.9, стр. 10. (Я имею в виду здесь Final Draft новой спецификации.) Однако все же это было неясно (достаточно ) для меня, и поскольку Visual С++ действовал иначе, чем мое понимание этого примера, я решил спросить.

Просьба указать правильные ссылки на технические характеристики.


Если вы назовете объект, он сохраняется для всего if (так что блок true и false блокируется, но уничтожается до окончания if).

Например:

if ( MyClass x = f() ) { /* ... */ } else { /* ... */ }
nextInstruction();

x может использоваться в обоих блоках if, но уничтожается до вызова nextInstruction.

Но что, если вы его не назовете?

if ( f() ) { /* ... */ } else { /* ... */ }
nextInstruction();

В моем понимании ссылочной части спецификации значение, возвращаемое f(), будет уничтожено до того, как выполнение вступит в один из блоков (либо для true, либо для false).

Однако Visual С++ уничтожает этот временный объект, как если бы он был назван. ( EDIT:, как сказал Тино Дидриксен, Visual С++ здесь хорошо работает. И действительно, теперь я подтверждаю это также. Я, должно быть, ошибся при просмотре результатов начальных тестов!)


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

Например, давайте:

class ScopedLock {
public:
  ~ScopedLock() { if ( isLocked() ) unlock(); }

  operator bool() const { return isLocked(); }

  /* ... */

};

Теперь, если у нас есть код вроде:

if ( ScopedLock lock = resource.lock() ) { /* ... */ }

мы можем быть уверены, что, когда выполнение входит в блок true, мы владеем ресурсом и что он не будет разблокирован, прежде чем мы покинем этот блок.

Но что, если кто-то написал это вот так:

if ( resource.lock() ) { /* ... */ }

Теперь важно, в какой момент будет вызван деструктор временного ScopedLock. Потому что он определяет, является ли этот код правильным или нет (в смысле использования ресурсов). (Опять же, давайте пропустим обсуждение того, плохо ли пишет такой код вообще. Это не вопрос этого вопроса...)

4b9b3361

Ответ 1

Насколько я могу судить, Visual С++ ошибается в этом отношении.

В самом деле, временное (почти всегда) разрушено в конце полного выражения, в котором оно создано:

§12.2/3: [...] Временные объекты уничтожаются как последний шаг при оценке полное выражение, которое (лексически) содержит точку, в которой они были созданы

Рассматривая определение оператора выбора (if и switch), мы можем видеть, что это условие является выражением:

§6.4:
selection-statement:
  if ( condition ) statement
  if ( condition) statement else statement
  switch ( condition ) statement

condition:
  expression
  type-specifier-seq declarator = assignment-expression

Таким образом, любые временные файлы, созданные в этом состоянии, должны быть уничтожены перед выполнением следующего оператора (если не привязаны к ссылке-на-const).

Поведение при вводе нового имени в условие указано в §6.4/3:

Имя, введенное декларацией в условии [...], находится в с момента его объявления до конца подзадач управляемый условием.

Итак, в вашем примере x находится в области видимости в двух ветвях if и уничтожается перед оценкой nextInstruction().

Ответ 2

Однако Visual С++ уничтожает этот временный объект, как если бы он был назван.

Нет, нет...

Учитывая код

#include <iostream>

struct S {
    S() { std::cout << "S()" << std::endl; }
    S(const S&) { std::cout << "S(const S&)" << std::endl; }
    ~S() { std::cout << "~S()" << std::endl; }
    operator bool() const { return true; }
};

int main() {
    std::cout << "main 1" << std::endl;

    if (S s = S()) {
        std::cout << "if 1" << std::endl;
    }
    else {
        std::cout << "else 1" << std::endl;
    }

    std::cout << "main 2" << std::endl;

    if (S()) {
        std::cout << "if 2" << std::endl;
    }
    else {
        std::cout << "else 2" << std::endl;
    }

    std::cout << "main 3" << std::endl;
}

GNU g++ 4.5.1 и g++ 4.7.0 и VС++ 2010 и VС++ 2012 имеют точный результат:

main 1
S()
if 1
~S()
main 2
S()
~S()
if 2
main 3

где именованное временное уничтожается после if/else, а неназванное временное уничтожается в конце условия.

Ответ 3

Это то, что я нашел в "Дизайн и эволюция С++" на стр. 145:

void h(String s1, String s2)
{
    const char* p;
    if (p = s1+s2) {
        // ...
    }
}

Выполняется ли уничтожение объекта, содержащего s1+s2 в конце условия или в конце всего оператора if? Ответ заключается в том, что объект, содержащий s1+s2, будет уничтожен в конце условия.