涉及优化器的局部变量构建和销毁

5
如果我有以下代码:
class A { ... };
class B { ... };

void dummy()
{
    A a(...);
    B b(...);
    ...
}

我知道变量ab将按相反的分配顺序被销毁(b将首先被销毁,然后是a);但是我能确定优化器永远不会交换ab的分配和构造吗?或者我必须使用volatile来强制执行?


6
我很确定这个订单是由规格标准保证的。 - Björn Pollex
很难想象如何交换实例化顺序会导致更优化的代码。显然,如果 b 在某种程度上依赖于 a,它就无法这样做——但为什么会这样呢? - wallyk
3
相当容易想象。事实上,我们很可能会想象到这些实例重叠。编译器生成两个汇编指令序列,优化器对它们进行重新排序。当第一个序列的最后一条指令依赖于前面的指令时,很可能会将其与第二个序列的第一个参数交换位置。优化器对这种值依赖关系有很好的理解。 - MSalters
4个回答

6
唯一保证的是,volatile对象的读写和I/O函数调用等任何可观察的副作用都会在构造b时发生之前发生在构造a时,而且b所需的a的任何副作用都将在需要它们之前发生。
很难想象为什么你需要比这更严格的顺序,但将对象设置为volatile将确保在初始化b的任何部分之前完全初始化a,虽然一些来自构造函数的代码仍然可能在a完成之前发生。

我需要严格的顺序,因为 A 在构造时锁定互斥量并在销毁时释放它,并且我需要在构建 B 时保持锁定。 - Loghorn
5
@AlessandroV: 对互斥锁进行加锁会产生内存屏障,提供所需的排序。尽管这超出了当前C++标准的范围,但是任何实现可用互斥锁的库都将确保这一点。 - Mike Seymour
1
(请注意,“当前的C++标准”是指C++03。 C++11确实要求锁定互斥体会产生适当的屏障。) - Mike Seymour

3
你唯一可以确定的是构建和分配 a 将在 b 之前。只要你用 ; 分隔语句,它们将按顺序执行,无论是否进行优化。 volatile 不会改变这一点,它的作用是防止编译器在访问之间缓存值。

1
“volatile” 还会对读写操作(相对于其他“volatile”对象)进行排序。 - Mike Seymour

0

好的,我在标准中找到了这样的语句,但其他人的回答让我有点困惑。

调用函数中的每个评估(包括其他函数调用),如果没有明确地在被调用函数的执行之前或之后特定排序,则与被调用函数的执行不确定地排序。 (换句话说,函数执行不会相互交错。)

它只确保函数调用不会相互交错,但函数的排序是不确定的,也就是说,

当A在B之前排序或B在A之前排序时,评估A和B是不确定排序的,但未指定哪个。


1
引号是指在同一完整表达式中有多个函数调用的情况,例如 int i = f() + g();。关键在于没有其他特定的顺序。分号明确地分隔了这两个表达式,前者在后者之前被排序。 - decltype

0
我遇到了一个问题。优化器重新排列了代码。为了防止重新排序,我建议两种方法:
1)将您的对象封装在结构中:
class A { ... };
class B { ... };

struct C {
     A a;
     B b;
};

void dummy()
{
    C c;
}

2) 把变量放在区块运算符内:

class A { ... };
class B { ... };

void dummy()
{
    A a;
    {
        B b;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接