在if条件表达式中构造的未命名临时的生存时间

时间:2012-11-30 10:46:22

标签: c++

标准如何定义在评估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()返回的值将在执行进入其中一个块(truefalse)之前被销毁。

然而,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的析构函数。因为它确定此代码是否正确(在资源使用方面)。 (再次让我们一起讨论编写这样的代码是否一般都不好。这不是这个问题的重点......)

3 个答案:

答案 0 :(得分:10)

  

然而,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和unnamed临时文件后销毁命名临时文件。

答案 1 :(得分:7)

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

实际上,在创建它的完整表达式结束时(几乎总是)会销毁一个临时文件:

  

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

查看选择语句(ifswitch)的定义,我们可以看到条件是一个表达式:

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

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

因此,在执行以下语句之前,应该销毁在条件中创建的任何临时值(除非绑定到const-to-const)。

在条件中引入新名称时的行为在§6.4/ 3中指定:

  

条件[...]中的声明引入的名称是   范围从声明点到子语句结束   由条件控制。

因此,在您的示例中,x位于if的两个分支的范围内,并在评估nextInstruction()之前销毁。

答案 2 :(得分:3)

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

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

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