I found that there is a different execution order between two similar statements(the only difference is the below one has an additional ;
). The destructor order is different. Does C++ have a corresponding specification about that or it's only an unspecified behavior?
Environment: GCC10
#include <iostream>
template <int num>
struct S {
S() {std::cout << "S(" << num << ")\n"; }
~S() {std::cout << "~S(" << num << ")\n"; }
};
int main() {
({S<1>(), S<2>();});
std::cout << "-----------------------------------\n";
({S<3>(), S<4>();;});
}
output:
S(1)
S(2)
~S(1)
~S(2)
-----------------------------------
S(3)
S(4)
~S(4)
~S(3)
This is not standard C++. This is a GCC extension known as statement expression. A compound statement enclosed in parentheses can appear where an expression is allowed. If the last statement in the brace-enclosed block is an expression statement, then the value of this expression is also the value of the overall statement expression; otherwise, the statement expression is of type void
and has no value.
Your first example is roughly equivalent to
([](){ return S<1>(), S<2>(); })();
(that's a lambda that's created and then called immediately). There's a comma expression that creates S<1>
and S<2>
temporaries. S<1>
is destroyed, S<2>
is, technically, copied to the return value - but that copy is elided. If it weren't for this copy elision, you'd see
S<1>()
S<2>()
S<2>(S<2>&&) // (1) move constructor
~S<2>() // (2) the original temporary is destroyed
~S<1>()
~S<2>() // the copy is destroyed outside of the lambda
But the pair (1)/(2) is elided, leaving the sequence you observe in your example.
In the second example, the last statement within the braces is not an expression, so the whole thing doesn't have a value either. It's roughly equivalent to
([](){ S<3>(), S<4>(); return; })();
Both temporaries are created and destroyed within the lambda, and the usual rules apply - temporaries are destroyed in the reverse order of construction.