是否应该保证复制省略?

时间:2018-05-30 13:58:00

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

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

2 个答案:

答案 0 :(得分:4)

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

  

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

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

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

Sander De Dycker在评论中指出, 已经有bug report来获得这个省长的机会。

答案 1 :(得分:3)

RVO(返回值优化)仅在从函数返回临时值时适用。在barbaz中,您都不会返回临时。而是返回一个名为的对象。这意味着您正在处理NRVO(命名返回值优化),这是不可保证的,并且更难做到。这两个输出都是标准兼容的,只是clang比gcc做得更好。