32

标准如何定义在评估if条件表达式期间构造的临时对象的生命周期?

我查找了这些信息,并在一个示例中找到了与第 10 页 1.9 美元中的 [10] 类似的内容。(我在这里指的是新规范的最终草案。)但对我来说仍然不清楚(足够)由于 Visual C++ 的行为与我对那个例子的理解不同,我决定问。

请提供适当的规范参考。


如果您命名该对象,它会在整个过程中持续存在if(因此既是true块又false是块,但在结束前被销毁if)。

例如:

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

x可以在两个if块中使用,但在nextInstruction被调用之前被破坏。

但是,如果你不命名呢?

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

在我对规范的引用部分的理解中,返回的值f()将在执行进入其中一个块( fortrue或 for false)之前被销毁。

但是,Visual C++ 会破坏该临时对象,就好像它已被命名一样。编辑:正如 Tino Didriksen 指出的那样,Visual C++ 在这里工作得很好。事实上,现在我也确认了这一点。在查看初始测试结果时我一定犯了一个错误!)


这在某些极端情况下确实很重要(这里不讨论它们的可能性有多大,或者以这种方式编写代码是否好......)。

例如让我们有:

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

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

  /* ... */

};

现在,如果我们有如下代码:

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

我们可以确定,当执行进入true块时,我们拥有该资源,并且在我们离开该块之前它不会被解锁。

但是如果有人这样写怎么办:

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

现在至关重要的是,临时的析构函数ScopedLock将被调用。因为它决定了这段代码是否正确(在资源使用方面)。(再次让我们跳过讨论编写这样的代码是否不好。这不是这个问题的重点......)

4

3 回答 3

12

但是,Visual C++ 会破坏该临时对象,就好像它已被命名一样。

不,它不...

给定代码

#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 以及 VC++ 2010 和 VC++ 2012 具有完全相同的输出:

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

其中命名的临时在 if/else 之后被销毁,未命名的临时在条件结束时被销毁。

于 2012-11-30T12:22:57.900 回答
7

据我所知,Visual C++ 在这方面是错误的。

事实上,一个临时的(几乎总是)在创建它的完整表达式的末尾被销毁:

§12.2/3:[...]临时对象被销毁作为评估完整表达式的最后一步,该完整表达式(词法上)包含创建它们的点

查看选择语句 ( ifand 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()

于 2012-11-30T11:43:31.823 回答
3

这是我在第 145 页的“C++ 的设计和演变”中发现的:

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

对象持有的销毁s1+s2发生在条件的末尾还是整个if语句的末尾?答案是持有的对象s1+s2将在条件结束时被销毁。

于 2012-11-30T12:02:47.613 回答