Domanda

Consider the following code:

#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();
}

Compiling with GCC and running gives the following output:

~C
~A
~B

Is it guaranteed that the destructors for temporary objects of types A, B and C will be called in this order when compiled with other compilers? In general, what is the order of destructor calls for temporaries if there is any?

È stato utile?

Soluzione

Let's talk about subexpressions and their sequencing. If E1 is sequenced before E2, that means E1 must be fully evaluated before E2 is. If E1 is unsequenced with E2, that means E1 and E2 may be evaluated in any order.

For f(A()) << B(), which is in your case the same as f(A()).operator<<(B()), we know that:

  • A() is sequenced before f(...),
  • f(...) is sequenced before operator<< and
  • B() is sequenced before operator<<

This also tells us that:

  • A() is sequenced before operator<<
  • A() is unsequenced with B()
  • f(...) is unsequenced with B()

If we assume RVO, so as not to complicate things, the possible order a compiler could evaluate the subexpressions in are:

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

The latter is the order observed in the OP. Note that the order of destruction is always the reverse order of construction.

Altri suggerimenti

The order of evaluation of expression f(A()) << B(); is not specified. Thus, the order of construction/destruction is not specified as well.

The order of evaluation of operands of << is unspecified. Hence, order is not guaranteed. Only the short-circuiting operators &&, ||, the ternary operator ?:, and the comma , operator has well-defined order of evaluation of operands. For others, it is not necessary that the left operand has to be evaluated before the right operand (or vice-versa).

Further, do not confuse operator precedence or associativity with order of evaluation. For a given expression E1 op E2, it is only necessary that before the operator op is applied, both E1 and E2 should be evaluated, but between themselves, E1 and E2 could be evaluated in any order.

Precedence rules decide the order in which operators are applied when there's more than 1 operator in an expression, such as E1 op1 E2 op2 E3.

Associativity is used to decide which operands bind to which operator, when the same operator is used more than once, that is, in E1 op E2 op E3, whether it is interpreted as (E1 op E2) op E3 or E1 op (E2 op E3).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top