3

说我有这个功能:

template <class A>
inline A f()
{
  A const r(/* a very complex and expensive construction */);

  return r;
}

r const由于const无法移动变量,因此声明是个好主意吗?请注意,返回值不是const. 我正在努力解决的问题是,r确实如此const,但将其声明为这样可能不是一个好主意。然而,限定符应该有助于编译器生成更好的代码。

4

2 回答 2

7

如此处所示,NRVO 省略r了该行所隐含的副本return r;

#include <iostream>

struct A {
  const char* name;
  A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
  A(A const&){ std::cout << "copied " << name << "\n"; }
  A(A &&){ std::cout << "moved " << name << "\n"; }
};

A f() {
  std::cout << "start of f()\n";
  A const r("bob");
  std::cout << "body of f()\n";
  return r;
}

int main() {
  A x = f();
}

并且其中的副本main也被省略了。

如果您以其他方式阻止 NRVO 和 RVO(例如-fno-elide-constructors在使用 GCC 编译时使用标志),则const可能会导致您的对象被复制而不是moved. 如果我们从以下位置删除复制构造函数,A您可以看到这一点:

#include <iostream>

struct A {
  const char* name;
  A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
  //A(A const&){ std::cout << "copied " << name << "\n"; }
  A(A &&){ std::cout << "moved " << name << "\n"; }
};

A f() {
  std::cout << "start of f()\n";
  A const r("bob");
  std::cout << "body of f()\n";
  return r;
}

int main() {
  A x = f();
}

代码不再编译。虽然只要 NRVO 发生就不会执行复制构造函数,但它的存在是const局部变量所必需的。

现在,NRVO 需要一些东西,例如沿相关函数的每个执行路径返回的单个变量:如果您曾经“中止”并执行 a return A(),则 NRVO 被阻塞,并且您的const局部变量突然强制复制所有返回站点。

于 2013-08-26T02:45:40.160 回答
1

如果class A在您的控制之下,并且您通过移动返回const对象,您可以这样做

mutable bool resources_were_stolen = false;

const并在移动构造函数中将其设置为 true

A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }

实际上,析构函数可能会变成if (resources_were_stolen) some_unique_ptr.release();,使用对象const在构造和销毁过程中失去它们的特性。

于 2013-08-26T02:54:42.443 回答