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

C++如何返回const引用传入的重量级对象且避免拷贝?

问题解答

问题根源

  • 当前代码触发拷贝构造的核心原因:std::move作用在const heavy_obj&类型的形参上时,得到的是const heavy_obj&&类型,而常规移动构造的形参是heavy_obj&&(非const右值引用),无法绑定到带const的右值,因此重载决议会降级选择接收const heavy_obj&的拷贝构造函数。
  • 不可以将移动构造改为heavy_obj(const heavy_obj&& t):移动构造的核心语义是转移原对象的资源,必然需要修改原对象的成员状态,添加const修饰后无法修改原对象,完全违背移动设计的初衷,除了极特殊的语义标记场景,不要使用这种写法。

解决方案

你可以通过完美转发实现同时接收左值、右值入参,且在入参为可修改右值时自动触发移动构造,仅在入参为const左值时才触发拷贝:

方案1:模板+完美转发(推荐,代码复用性高)

#include <iostream>
#include <utility>
#include <type_traits>

class heavy_obj {
public:
    heavy_obj() {
        std::cout << "Standard" << std::endl;
    }

    heavy_obj(const heavy_obj&) {
        std::cout << "Copy" << std::endl;
    }

    heavy_obj(heavy_obj&&) noexcept {
        std::cout << "Move" << std::endl;
    }
};

template<typename T>
std::decay_t<T> return_obj_or_default(T&& t, bool ret) {
    if(ret) {
        // 保留入参的原始值类型和const属性,右值则移动,左值则拷贝
        return std::forward<T>(t);
    } else {
        return heavy_obj();
    }
}

int main()
{
    heavy_obj t;
    std::cout << "Test1: 传入左值" << std::endl;
    heavy_obj t2 = return_obj_or_default(t, true); // 入参为左值,调用拷贝构造
    std::cout << "Test2: 传入右值" << std::endl;
    heavy_obj t3 = return_obj_or_default(heavy_obj(), true); // 入参为右值,调用移动构造
    return 0;
}

输出结果:

Standard
Test1: 传入左值
Copy
Test2: 传入右值
Standard
Move

符合预期:右值入参触发移动,无额外拷贝开销。

方案2:函数重载(无需模板,逻辑更直观)

如果不想使用模板,也可以写两个重载函数分别处理左值和右值场景:

// 处理const左值入参,只能拷贝
heavy_obj return_obj_or_default(const heavy_obj& t, bool ret) {
    if(ret) return t;
    return heavy_obj();
}

// 处理右值入参,可以移动
heavy_obj return_obj_or_default(heavy_obj&& t, bool ret) {
    if(ret) return std::move(t);
    return heavy_obj();
}

效果和模板方案完全一致。

补充说明

如果传入的是const修饰的左值,无论用哪种方案都会触发拷贝,这是符合C++语义的:const对象不允许被修改,自然不能转移它的资源,无法触发移动构造。

内容的提问来源于stack exchange,提问作者Mike van Dyke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 20:12:01