标准如何定义在评估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()
返回的值将在执行进入其中一个块(true
或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
的析构函数。因为它确定此代码是否正确(在资源使用方面)。 (再次让我们一起讨论编写这样的代码是否一般都不好。这不是这个问题的重点......)
答案 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:[...]临时对象作为评估的最后一步被销毁 表达式(词法上)包含它们的点 被创造了
查看选择语句(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-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
的对象将在条件结束时被销毁。