2

我发现两个相似的语句之间有不同的执行顺序(唯一的区别是下面的语句有一个附加的;)。析构函数的顺序不同。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)
4

1 回答 1

6

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

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

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

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

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 中创建和销毁的,并且适用通常的规则 - 临时对象以与构造相反的顺序被销毁。

于 2020-10-26T03:22:38.860 回答