C++中如何返回字符串tuple且避免临时额外拷贝?
问题:返回tuple时如何避免string的额外拷贝?
假设使用C++20标准,以下代码在Visual Studio 2022调试时,发现string的析构函数被调用两次,且每次都释放堆内存:
#include <iostream> #include <tuple> using namespace std; static tuple<string> get_tuple() { string arg = "This is a long string allocated on the heap"; return { arg }; } void main() { auto [x] = get_tuple(); cout << "x: " << x << endl; }
但直接返回string时,却能通过移动构造避免额外拷贝:
#include <iostream> using namespace std; static string get_tuple() { string arg = "This is a long string allocated on the heap"; return arg; } void main() { auto x = get_tuple(); cout << "x: " << x << endl; }
为什么tuple无法做到?是否有办法在返回tuple或类似类型时消除这份额外拷贝?
解答
问题根源
问题出在return { arg };这一行:
- 直接返回
string时,编译器会触发返回值优化(RVO);即便RVO不适用,也会自动将return arg转换为移动构造(因为arg是即将销毁的局部变量,属于将亡值)。 - 返回
tuple<string>时,{ arg }会先以拷贝构造的方式创建一个string临时对象,再用这个临时对象初始化tuple的成员。这里arg是左值,编译器不会自动将其转为右值,因此产生了一次不必要的拷贝。
解决方法
有几种方式可以消除这个额外拷贝:
1. 显式移动构造tuple成员
修改返回语句,用std::move将arg转为右值,让tuple的成员通过移动构造初始化:
#include <iostream> #include <tuple> #include <utility> // 引入std::move using namespace std; static tuple<string> get_tuple() { string arg = "This is a long string allocated on the heap"; return { move(arg) }; } void main() { auto [x] = get_tuple(); cout << "x: " << x << endl; }
这样arg的堆内存资源会被直接转移到tuple的string成员中,不会发生拷贝。
2. 直接构造tuple,避免临时对象
可以用make_tuple配合std::move,或者直接在返回时就地构造tuple:
// 方式一:使用make_tuple static tuple<string> get_tuple() { string arg = "This is a long string allocated on the heap"; return make_tuple(move(arg)); } // 方式二:直接构造,省略局部变量 static tuple<string> get_tuple() { return { "This is a long string allocated on the heap" }; }
第二种方式会直接在tuple的成员位置构造string,连局部变量arg都不需要,彻底避免拷贝和移动操作。
3. 利用返回值优化(RVO)构造tuple
编译器对tuple的返回同样支持RVO,只要构造过程符合RVO条件——比如直接在返回语句中构造tuple,而非先创建局部tuple再返回:
static tuple<string> get_tuple() { return tuple<string>{ "This is a long string allocated on the heap" }; }
这种写法本质和方式二一致,核心都是让tuple成员直接从纯右值初始化,跳过中间拷贝步骤。
关键说明
- 即便类型有移动构造函数,编译器也不会自动将左值转为右值(左值代表对象后续可能被使用,编译器不能随意转移其资源),必须显式调用
std::move。 - 返回tuple时,只要确保初始化tuple成员的是右值(将亡值或纯右值),就能触发移动构造或直接初始化,消除额外的内存拷贝。
内容的提问来源于stack exchange,提问作者mark
相关产品推荐
相关产品推荐

