You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用std::make_pair返回pair时结构化绑定无法触发RVO?

std::pair返回场景下拷贝构造触发的原因说明

你对RVO(返回值优化)的适用边界存在认知偏差,C++标准规定的强制拷贝消除仅覆盖极特定的场景,结构化绑定配合std::pair返回值的场景并不完全在强制消除范围内。

强制拷贝消除的标准生效场景

从C++17版本开始,编译器必须执行拷贝/移动构造消除的场景只有两类:

  • return语句的操作数是与函数返回值类型完全一致的纯右值(prvalue)
  • 变量初始化时,初始化表达式是与变量类型完全一致的纯右值

你写的第二个测试用例完全符合第一类规则:func返回类型为Test,return的操作数是Test()这个同类型纯右值,因此拷贝构造被强制消除,全程不会触发拷贝调用。

pair场景下两次拷贝的真实来源

你观察到的两次拷贝构造调用,既不是返回值传递过程产生的,也不是结构化绑定产生的,根源来自std::make_pair的实现逻辑和你的Test类型的定义:

  1. std::make_pair的核心逻辑是接收传入参数,将其构造为std::pair内部存储的两个成员对象。你传入的两个Test()是临时纯右值,但由于你自定义了拷贝构造函数,编译器不会为Test自动生成默认移动构造函数,因此make_pair在构造pair内部成员时,无法调用移动构造,只能退而求其次调用拷贝构造,这就是两次输出的来源。
  2. 实际上make_pair返回的std::pair<Test, Test>纯右值本身是会被RVO优化的,会直接构造在main函数的栈帧内存中;后续的结构化绑定也只是直接把名字a、b绑定到这个pair的两个内部成员上,不会额外产生拷贝操作。

验证与优化方案

你可以通过两个简单测试验证上述结论:

  • 为Test添加默认移动构造声明Test(Test&&) = default;,重新编译运行就会发现拷贝构造不再被调用(移动构造的调用通常也会被编译器进一步优化消除)
  • 如果要完全避免构造过程中的移动/拷贝,可以使用分段构造方式直接在pair的内存位置就地构造对象,修改func实现如下:
#include <iostream>
#include <utility>
#include <tuple>

using namespace std;

struct Test {
    Test() {}
    Test(const Test &other) {cout << "Test copy constructor called\n";}
};

auto func() {
    // 直接在pair的成员存储位置构造两个Test对象,无拷贝/移动
    return pair<Test, Test>(piecewise_construct, tuple<>(), tuple<>());
}

int main()
{
    auto [a, b] = func();
    return 0;
}

编译运行上述代码,不会有任何拷贝构造的输出。


内容的提问来源于stack exchange,提问作者Christopher Miller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 04:31:12