5

考虑以下代码:

#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 类型的临时对象的析构函数?一般来说,如果有的话,对临时对象的析构函数调用的顺序是什么?

4

3 回答 3

10

让我们谈谈子表达式及其排序。如果E1is之前排序 E2,则意味着E1必须在 is 之前进行全面评估E2。如果E1 E2排序,则表示E1并且E2可以按任何顺序进行评估。

对于f(A()) << B(),在您的情况下与 相同f(A()).operator<<(B()),我们知道:

  • A()之前排序f(...)
  • f(...)排序之前operator<<
  • B()之前排序operator<<

这也告诉我们:

  • A()之前排序operator<<
  • A()是无序的B()
  • f(...)是无序的B()

如果我们假设 RVO,为了不使事情复杂化,编译器评估子表达式的可能顺序是:

  • A()-> f(...)-> B(),产生~B()-> ~C()->~A()
  • A()-> B()-> f(...),产生~C()-> ~B()->~A()
  • B()-> A()-> f(...),产生~C()-> ~A()->~B()

后者是在 OP 中观察到的顺序。请注意,销毁的顺序始终是构造的相反顺序。

于 2012-12-11T16:10:18.803 回答
3

f(A()) << B();未指定表达式的评估顺序。因此,也没有指定构建/销毁的顺序。

于 2012-12-11T15:54:01.233 回答
2

的操作数的评估顺序<<未指定。因此,不能保证顺序。只有短路运算符&&, ||, 三元运算符?:和逗号,运算符具有明确定义的操作数求值顺序。对于其他人,没有必要在右操作数之前评估左操作数(反之亦然)。

此外,不要将运算符优先级或关联性与评估顺序混淆。对于给定的表达式E1 op E2,只需要在op应用运算符之前,都应该计算E1和,但是在它们之间,并且可以以任何顺序计算。E2E1E2

当表达式中有多个运算符时,优先规则决定了运算符的应用顺序,例如E1 op1 E2 op2 E3.

关联性用于决定哪些操作数绑定到哪个运算符,当同一个运算符被多次使用时,即在 中E1 op E2 op E3,是否解释为(E1 op E2) op E3E1 op (E2 op E3)

于 2012-12-11T15:54:17.123 回答