return 语句中带括号的 GCC C++11/14 和 RVO

GCC C++11/14 and RVO in return statement with parentheses

我观察到依赖 -std=c++11-std=c++14/1z 标志 gcc(5.4 和 7.3)的情况(我的理解)在这种情况下应用 RVO 或不应用当 return 语句表达式周围有括号时。 clang 似乎在两种情况下发出使用 RVO 的相同代码。

请考虑以下几点:

#include <iostream>

class C {
  public:
    C(){ std::cout << "ctor" << std::endl; }
    C(C const &){ std::cout << "copy ctor" << std::endl; }
    C(C &&){ std::cout << "move ctor" << std::endl; }
    C & operator=(C const &){ std::cout << "copy =" << std::endl; return *this; }
    C & operator=(C &&){ std::cout << "move =" << std::endl; return *this; }
    ~C() { std::cout << "dtor" << std::endl; }
};

C f1() { C c; return c; }

C f2() { C c; return (c); } // <--- added parentheses;

int main() {
  C c1{}, c2{};

  std::cout << "--- f1 ---" << std::endl;
  c1 = f1();
  std::cout << "--- f2 ---" << std::endl;
  c2 = f2();
  std::cout << "---" << std::endl;
}

对于 g++ -std=c++11 -O3 -Wall -Wextra -Wpedantic 输出是:

ctor
ctor
--- f1 ---
ctor
move =
dtor
--- f2 ---
ctor
move =
dtor
---
dtor
dtor

对于 g++ -std=c++14 -O3 -Wall -Wextra -Wpedantic 输出是:

ctor
ctor
--- f1 ---
ctor
move =
dtor
--- f2 ---
ctor
move ctor
dtor
move =
dtor
---
dtor
dtor

这是 f1()f2() 的相关汇编器(参见 godbolt.org):

f1():
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov rdi, rax
  call C::C()
  nop
  mov rax, QWORD PTR [rbp-8]
  leave
  ret
f2():
  push rbp
  mov rbp, rsp
  push rbx
  sub rsp, 40
  mov QWORD PTR [rbp-40], rdi
  lea rax, [rbp-17]
  mov rdi, rax
  call C::C()
  lea rdx, [rbp-17]
  mov rax, QWORD PTR [rbp-40]
  mov rsi, rdx
  mov rdi, rax
  call C::C(C&&)
  nop
  lea rax, [rbp-17]
  mov rdi, rax
  call C::~C()
  jmp .L12
  mov rbx, rax
  lea rax, [rbp-17]
  mov rdi, rax
  call C::~C()
  mov rax, rbx
  mov rdi, rax
  call _Unwind_Resume

问题基本上是,发生了什么事?为什么 RVO (?) 在 gcc -std=c++14/1z 中的 return 语句括号处中断?我知道 decltype(auto) x4d = (i); // decltype(x4d) is int& 的情况,但这里不应该是这种情况。

考虑到有多少代码可能使用括号并从 c++11 切换到 c++14 编译标志,其影响有点严重。

谢谢!

根据 C++ 标准,return c;return (c); 之间的行为没有区别。 C++11、14、17都在括号表达式的定义下说:

The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

并且规范的复制省略部分没有任何内容"otherwise indicated"。


由于复制省略是可选的,因此 gcc 这样做并没有违反标准。但也许可以将其视为实施质量错误。

没有理由写 return (c); 而不是 return c; ,因此您可以通过 grep 代码库 return ( 并删除来安全地处理问题多余的括号。