保证复制省略不应该适用吗?

Shouldn't guaranteed copy elision apply?

本文关键字:不应该 复制省      更新时间:2023-10-16

我不明白 gcc 在这里的行为,我希望 RVO 适用,但无论我是否传递优化标志和/或传递-std=c++17,在第二种情况下,无端的大括号似乎阻止了 GCC 省略副本。

$ cat /tmp/foo.cc
#include <iostream>
#define PING() std::cerr << __PRETTY_FUNCTION__ << 'n'
struct foo
{
foo() { PING(); }
~foo() { PING(); }
foo(const foo&) { PING(); }
};
foo bar()
{
PING();
foo res;
return res;
}
foo baz()
{
PING();
{
foo res;
return res;
}
}
int main()
{
foo f1 = bar();
foo f2 = baz();
}
$ g++-mp-7 -std=c++17 -O3 foo.cc
$ ./a.out
foo bar()
foo::foo()
foo baz()
foo::foo()
foo::foo(const foo&)
foo::~foo()
foo::~foo()
foo::~foo()

这不应该是保证复制的一部分吗? Clang的行为符合我的预期:

$ clang++-mp-4.0 foo.cc
$ ./a.out
foo bar()
foo::foo()
foo baz()
foo::foo()
foo::~foo()
foo::~foo()

来自 https://en.cppreference.com/w/cpp/language/copy_elision

在以下情况下,即使复制/移动(自 C++11(构造函数和析构函数具有可观察到的副作用,编译器也允许,但不需要省略类对象的复制和移动(自 C++11 起(构造。这是一个优化:即使它发生了并且没有调用 copy-/move-constructor,它仍然必须存在并且可访问(好像根本没有发生优化(,否则程序格式不正确。

  • 如果函数按值返回类类型,并且 return 语句的表达式是具有自动存储持续时间的非易失性对象的名称,该对象不是函数参数或 catch 子句参数,并且与函数的返回类型具有相同的类型(忽略顶级 cv-限定(,则省略复制/移动(自 C++11 以来(。构造该本地对象时,它直接在存储中构造,否则函数的返回值将被移动或复制到该存储中。这种复制省略的变体称为NRVO,即"命名返回值优化"。

NRVO 不能保证会发生,但在您的情况下是可能的。

正如Sander De Dycker在评论中指出的那样, 已经有一个错误报告来获得这个Elision的机会。

RVO(返回值优化(仅在从函数返回临时值时适用。 在barbaz您都不会返回临时。 相反,您返回一个命名对象。 这意味着您正在处理 NRVO(命名返回值优化(,这是无法保证的,而且更难做到。 两个输出都符合标准,只是 clang 在优化方面比 gcc 做得更好。