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?
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<<
andB()
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.