请考虑以下代码:
#include <iostream>
struct A {
~A() { std::cout << "~A" << std::endl; }
};
struct B {
~B() { std::cout << "~B" << std::endl; }
};
struct C {
~C() { std::cout << "~C" << std::endl; }
void operator<<(const B &) {}
};
C f(const A &a = A()) {
return C();
}
int main() {
f(A()) << B();
}
使用GCC进行编译并运行会提供以下输出:
~C
~A
~B
是否可以保证在与其他编译器一起编译时,将按此顺序调用类型A,B和C的临时对象的析构函数?一般来说,如果有的话,析构函数调用的顺序是什么?
答案 0 :(得分:10)
让我们谈谈子表达式及其排序。如果E1
在 E2
之前排序,则表示必须在E1
之前对E2
进行全面评估。如果E1
未按 E2
排序,则表示可以按任何顺序评估E1
和E2
。
对于f(A()) << B()
,在您的情况下与f(A()).operator<<(B())
相同,我们知道:
A()
在f(...)
,f(...)
在operator<<
和B()
在operator<<
这也告诉我们:
A()
在operator<<
A()
未按B()
排序
{li> f(...)
未按B()
排序
如果我们假设RVO,为了不使事情复杂化,编译器可以评估子表达式的可能顺序是:
A()
- &gt; f(...)
- &gt; B()
,产生~B()
- &gt; ~C()
- &gt; ~A()
A()
- &gt; B()
- &gt; f(...)
,产生~C()
- &gt; ~B()
- &gt; ~A()
B()
- &gt; A()
- &gt; f(...)
,产生~C()
- &gt; ~A()
- &gt; ~B()
后者是在OP中观察到的顺序。请注意,破坏的顺序始终与构造的顺序相反。
答案 1 :(得分:3)
未指定表达式f(A()) << B();
的评估顺序。因此,也没有规定构造/破坏的顺序。
答案 2 :(得分:2)
<<
的操作数的评估顺序未指定。因此,订单无法保证。只有短路运算符&&
,||
,三元运算符?:
和逗号,
运算符具有明确定义的操作数评估顺序。对于其他人,不必在右操作数之前评估左操作数(反之亦然)。
此外,不要将运算符优先级或关联性与评估顺序混淆。对于给定的表达式E1 op E2
,只需要在应用运算符op
之前,应评估E1
和E2
,但在它们之间,{{1} }和E1
可以按任何顺序进行评估。
优先级规则决定在表达式中有多个运算符时应用运算符的顺序,例如E2
。
关联性用于决定哪个操作数绑定到哪个运算符,当同一个运算符被多次使用时,即在E1 op1 E2 op2 E3
中,是否被解释为E1 op E2 op E3
或{{1} }。