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
E1is sequenced beforeE2, that meansE1must be fully evaluated beforeE2is. IfE1is unsequenced withE2, that meansE1andE2may be evaluated in any order.For
f(A()) << B(), which is in your case the same asf(A()).operator<<(B()), we know that:A()is sequenced beforef(...),f(...)is sequenced beforeoperator<<andB()is sequenced beforeoperator<<This also tells us that:
A()is sequenced beforeoperator<<A()is unsequenced withB()f(...)is unsequenced withB()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.