Class 包含字符串:当它从函数返回时究竟发生了什么?

Class containing string: what really happens when it's returned from a function?

并不是说我不信任我的编译器,而是我想知道发生了什么。假设我有

struct Foo {
  std::string s;
};

我想创建其中一个(在堆栈上),填写很长的字符串,然后 return 从我的函数中获取它。

Foo f() {
  Foo foo {my_very_long_string};
  return foo;
  // OR: return Foo {my_very_long_string};
}

我知道有 RVO 和移动语义之类的东西;我怎么知道它们正在被使用,并且在运行时它不会在堆上分配一个包含数据的新字符串,复制它并释放旧字符串? (除了我的程序会变慢。)

是否使用移动构造函数来重用字符串数据?还是使用 RVO 实际上 return 相同的字符串?

NRVO 或移动命名对象

在函数中:

Foo f() {
  Foo foo{my_very_long_string};
  return foo;
}

对象foo有一个名称(即:foo),它是一个已命名的对象。

命名 RVO (NRVO),这是一个可选的优化,可能会发生。如果没有发生 NRVO,则移动 foo,因为它是一个本地对象,因此在此上下文中被视为右值(即:return 语句)。


RVO/copy 未命名对象的移出或移动

然而,在函数中:

Foo f() {
  return Foo{my_very_long_string};
}

一个未命名的对象,这是Foo{my_very_long_string}的结果。

  • 自 C++17 起,必须删除副本(即:与 RVO 效果相同,但语义不同)。

  • 在C++17之前,可能会出现当时可选优化的RVO。如果不是,则移动它,因为 Foo{my_very_long_string} 已经是一个右值。


在上述任何情况下都不会为新字符串分配堆。