包含字符串的类:当它从函数返回时会发生什么?

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

本文关键字:返回 什么 函数 字符串 包含      更新时间:2023-10-16

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

struct Foo {
  std::string s;
};

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

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

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

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

NRVO 或移动命名对象

在函数中:

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

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

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


RVO/复制或移动未命名的对象

但是,在函数中:

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

涉及一个未命名的对象,即由Foo{my_very_long_string}生成的对象。

  • 从 C++17 开始,必须省略副本(即:与 RVO 相同的效果,尽管语义不同(。

  • 在 C++17 之前,可能会发生 RVO,这在当时是可选的优化。如果没有,则将其移动,因为Foo{my_very_long_string}已经是右值。


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