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}
已经是一个右值。
在上述任何情况下都不会为新字符串分配堆。
并不是说我不信任我的编译器,而是我想知道发生了什么。假设我有
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}
已经是一个右值。
在上述任何情况下都不会为新字符串分配堆。