是否可以确保在下一次迭代之前调用循环内局部对象的析构函数?


11

当我有一个循环并在此循环内时,创建一个新的堆栈变量(不将其分配到堆上,也不将其持有的变量保持在循环体内声明),是否保证此对象的析构函数在下一次迭代开始之前被调用,否则可能循环展开由编译器更改有关的东西吗?


1
循环展开本身不会改变执行顺序。但是,循环并行化可以做到这一点。
阿德里安·摩尔

Answers:


8

来自n4800

§6.3.3 块范围

在块(8.3)中声明的名称是该块的本地名称;它具有块范围。它的潜在范围从其声明点(6.3.2)开始,到其块末尾结束。在块作用域中声明的变量是局部变量。

§10.3.6 析构函数:

当创建对象的块退出时,析构函数将隐式调用[...](8.7)

§4.1.1 摘要机

这项规定有时称为“按原样”规则,因为只要可以从以下行为的可观察行为中确定只要实现可以遵循该要求,则实现可以自由地忽略本文档的任何要求。该程序

[强调我的]

所以,是的。您的变量在循环末尾(是一个块)超出范围,因此,只要观察程序行为的人都能知道,则调用其析构函数。


1
什么时候调用析构函数都没有。
形成鲜明

2
@stark允许他们执行此操作的是常规规则。该标准仅指定抽象机的行为。不知道是否有必要在这里回答所有这些细节。
Max Langhof

2
@ stark,IMO,与问题无关。您可能还说,析构函数可能是内联的,因此根本不使用called。或者,如果它们有效地(按规则)不执行任何操作,则可能没有针对此类析构函数生成的程序集。
丹尼尔·兰格


2
@stark在哪里定义什么?请注意,此讨论与该问题无关。您可以询问有关此问题的另一个单独的问题。
丹尼尔·兰格

8

是。当您考虑在其中声明变量的“块”(即,在一对大括号之间)时,更容易可视化。该循环本身就是一个块,当它到达结束括号时,在下一次迭代之前,将调用循环中声明的所有自动存储变量的析构函数。

可能会由编译器循环展开,从而对此有所改变?

根据经验,不要考虑编译器将优化什么,因为无论优化它做什么,它仍然需要保证程序的行为。在这种情况下,如果发生循环展开,则不会改变任何效果。


2
根据+1的经验法则,编写代码时不必担心编译器内部问题。本来打算以同样的精神为我的回答添加一些内容,但是现在它已经存在了
idclev 463035818 '19

复制删除和RVO会更改程序的行为,不是吗?
Jean-BaptisteYunès19年

@让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”的析构函数是否正确调用?
user1282931

我也不是100%肯定。如果发现错误,请更正我的答案。:)
朱利安·施纳贝尔


0

当然,在迭代结束时调用dtor,并且展开循环不应修改此行为,就像进行其他任何优化(优化不应修改程序行为)一样,除了某些RVO之类的方法(它可以消除一些语义上虚假的对象创建) 。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.