保证代码按照您的意愿运行。
这种保证在 C++ 中很重要,因为 C++ 不是函数式编程语言,因为几乎 C++ 中的任何函数都可能有副作用(来自当前线程的执行流程,或者来自其他线程甚至其他进程) , 无论数据是否声明为volatile)。因此,语言规范保证了完整表达式的顺序。
为了将 C++11 标准拼凑起来,有许多子句必须一起考虑。
最重要的条款是§1.9:
§1.9程序执行 [intro.execution]
1 本国际标准中的语义描述定义了一个参数化的非确定性抽象机。本国际标准对一致性实现的结构没有要求。特别是,它们不需要复制或模仿抽象机器的结构。相反,需要符合要求的实现来模拟(仅)抽象机的可观察行为,如下所述。* (<-- 脚注在标准本身)
* 该规定有时被称为“好像”规则,因为只要从可观察到的结果可以确定,只要结果就好像该要求已被遵守,实施就可以自由地无视本国际标准的任何要求程序的行为。例如,如果一个实际的实现可以推断出它的值没有被使用并且没有产生影响程序可观察行为的副作用,则它不需要评估表达式的一部分。
(文本的粗体是我的。)
本条款提出了与该问题相关的两个重要要求。
如果一个表达式可能有副作用,它将被评估。在您的情况下,表达式scoped_timer a;可能有副作用,因此将对其进行评估。
“ ...符合要求的实现需要模拟(仅)抽象机的可观察行为,如下所述。 ”,其中“以下”包括同一部分的第 13 和 14 条:
§1.9.13之前排序是由单个线程 (1.10) 执行的评估之间的不对称、传递、成对关系,这会在这些评估之间产生偏序。给定任意两个评估 A 和 B,如果 A 在 B 之前排序,则 A 的执行将在 B 的执行之前。如果 A 没有在 B 之前排序并且 B 没有在 A 之前排序,那么 A 和 B 是无序的。[注意:未排序评估的执行可以重叠。—尾注] 当 A 在 B 之前排序或 B 在 A 之前排序时,评估 A 和 B 的排序不确定,但未指定哪个。[注意:不确定顺序的评估不能重叠,但可以先执行。——尾注]
§1.9.14与完整表达式关联的每个值计算和副作用在与要评估的下一个完整表达式关联的每个值计算和副作用之前排序。* (<-- 这里的脚注不相关)
因此,您的表达式scoped_timer a;(这是一个完整的表达式)可能会产生副作用并将被评估;因此, 的值的计算a将在块中的以下任何语句之前进行排序。
关于对象的销毁a,这更简单。
§3.7.3.3如果具有自动存储持续时间的变量具有初始化或具有副作用的析构函数,则不应在其块结束之前将其销毁,即使看起来未使用也不应作为优化消除,除非可以按照 12.8 中的规定消除类对象或其复制/移动。
这清楚地表明在块退出之前不会调用析构函数。
附录并确认所有块级变量在块范围的末尾被销毁(并调用它们的析构函数),这里是 C++11 标准:
§3.7.3.1显式声明的块范围变量 register 或未显式声明的 static 或 extern 具有自动存储持续时间。这些实体的存储一直持续到创建它们的块退出。
§3.7.3.2 [注意:这些变量如 6.7 所述被初始化和销毁。——尾注]
...以及上述§6.7:
§6.7.2具有自动存储持续时间(3.7.3)的变量在每次执行其声明语句时都会被初始化。在块中声明的具有自动存储持续时间的变量在退出块时被销毁(6.6)。
该块在此处定义为一对花括号之间的所有代码{}:
§6.3.1为了可以在需要一个的地方使用多个语句,提供了复合语句(也等效地称为“块”)。
compound-statement:
{ statement-seq }
statement-seq:
statement
statement-seq statement
复合语句定义块作用域 (3.3)。
注意:compount-statement(etc) 部分需要一段时间才能习惯,但重要的一点是,这里的开花括号{和闭花括号}实际上是指代码中的字面开花括号和闭花括号。这正是 C++11 标准block中将作用域定义为花括号之间的语句序列的确切位置。
将各个部分放在一起:因为上面引用的标准是这样说The storage for these entities lasts until the block in which they are created exits的,所以Variables with automatic storage duration declared in the block are destroyed on exit from the block您可以放心,a您的问题中的对象(以及任何块级对象)将持续到块结束,并且将被销毁并拥有它的块退出时调用的析构函数。