GCC 语句表达式中的执行顺序

Execution order in GCC statement expressions

我发现两个相似的语句之间有不同的执行顺序(唯一的区别是下面的语句多了一个;)。析构函数顺序不同。 C++ 是否对此有相应的规范,或者它只是一个未指定的行为?

环境: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>();;});
}

输出:

S(1)
S(2)
~S(1)
~S(2)
-----------------------------------
S(3)
S(4)
~S(4)
~S(3)

这不是标准的 C++。这是一个名为 statement expression 的 GCC 扩展。括号中的复合语句可以出现在允许表达式的地方。如果brace-enclosed块中的最后一条语句是一个表达式语句,那么这个表达式的值也是整个语句表达式的值;否则,语句表达式是 void 类型并且没有值。

你的第一个例子大致相当于

([](){ return S<1>(), S<2>(); })();

(这是创建后立即调用的 lambda)。有一个逗号表达式可以创建 S<1>S<2> 临时值。 S<1> 被销毁,S<2> 在技术上被复制到 return 值 - 但该副本被删除。如果不是这个复制省略,你会看到

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

但是 (1)/(2) 对被省略,留下您在示例中观察到的序列。

在第二个例子中,大括号中的最后一个语句不是表达式,所以整个语句也没有值。大致相当于

([](){ S<3>(), S<4>(); return; })();

两个临时对象都在 lambda 中创建和销毁,并且适用通常的规则 - 临时对象以相反的构造顺序销毁。