【问题标题】:Shouldn't guaranteed copy elision apply?不应该保证复制省略适用吗?
【发布时间】:2018-05-30 13:58:00
【问题描述】:

我不明白 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()

【问题讨论】:

  • RVO 不是只保证匿名对象吗? barbaz 都有命名实例
  • @UnholySheep 在这种情况下,clang 对 NRVO 的生命周期分析看起来更好。我们不知道 gcc 版本。
  • NRVO 没有保证。所以两者都是有效的,只是 clang 做得更好。

标签: c++ c++17 copy-elision


【解决方案1】:

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

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

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

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

正如 Sander De Dycker 在评论中指出的那样, 已经有bug report 可以得到这个省略的机会。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2023-01-30
      • 1970-01-01
      相关资源
      最近更新 更多