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

如何用单个函数实现兼具完美转发与拷贝消除的do_smth_and_pass?

问题描述

我已经实现了一个函数copy_if_lvalue_or_const_ref,逻辑是:传入可变右值引用时返回右值引用,否则返回参数的拷贝。实现代码如下:

#include <type_traits>
#include <utility>

template <typename T>
constexpr inline auto is_const_reference_v = std::is_const_v<std::remove_reference_t<T>>;

template <typename T>
constexpr inline auto is_mutable_rvalue_reference_v =
    std::is_rvalue_reference_v<T&&> && !is_const_reference_v<T>;

// 转发可变右值引用
template <typename T, class =
    std::enable_if_t<is_mutable_rvalue_reference_v<T>>
>
T&& copy_if_lvalue_or_const_ref (T&& t)
{
    return std::move(t);
}

// 拷贝其他所有情况
template <typename T>
std::decay_t<T> copy_if_lvalue_or_const_ref (T& t)
{
    return t;
}

接下来我想实现函数do_smth_and_pass:它需要将copy_if_lvalue_or_const_ref的结果存入临时变量,调用变量的do_smth方法后返回该变量。我尝试了单函数实现:

template <typename T>
decltype(auto) do_smth_and_pass(T&& t)
{
    decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
    tmp.do_smth();
    if constexpr (is_mutable_rvalue_reference_v<T>)
    {
        return static_cast<T&&>(tmp);
    }
    else
    {
        return tmp; // 无法触发拷贝消除,会调用移动构造函数
    }
}

但这个实现里,针对左值的return tmp无法触发拷贝消除,会额外调用移动构造函数。我通过两个重载函数实现了需求:

template <typename T, std::enable_if_t<is_mutable_rvalue_reference_v<T>>* = nullptr>
decltype(auto) do_smth_and_pass(T&& t)
{
    decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
    tmp.do_smth();
    return static_cast<T&&>(tmp);
}

template <typename T, std::enable_if_t<!is_mutable_rvalue_reference_v<T>>* = nullptr>
std::decay_t<T> do_smth_and_pass(T&& t)
{
    decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
    tmp.do_smth();
    return tmp;
}

但我觉得这种重载方式不够通用,想问是否可以用单个函数实现该逻辑?

解决方案

可以通过提前推导返回值类型,让临时变量与返回值类型完全匹配,从而实现单函数版本并触发拷贝消除。修改后的代码如下:

#include <type_traits>
#include <utility>

// 保留原有的copy_if_lvalue_or_const_ref实现...

template <typename T>
decltype(auto) do_smth_and_pass(T&& t)
{
    // 提前推导函数的返回值类型
    using ReturnType = decltype(copy_if_lvalue_or_const_ref(std::forward<T>(t)));
    
    // 让tmp直接是返回值类型的对象/引用
    ReturnType tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
    tmp.do_smth();
    
    if constexpr (std::is_rvalue_reference_v<ReturnType>)
    {
        // 右值引用情况:转换为右值引用返回,保留转发语义
        return static_cast<ReturnType>(std::move(tmp));
    }
    else
    {
        // 值类型情况:tmp与返回值类型一致,可触发拷贝消除(RVO)
        return tmp;
    }
}

关键说明

  1. 类型匹配触发拷贝消除:当临时变量tmp的类型与函数返回值类型完全一致时,return tmp满足返回值优化(RVO)的条件——编译器可以直接将tmp构造到调用方的返回值存储位置,避免拷贝或移动操作。
  2. 右值引用的正确转发:对于右值引用类型的tmp,需要用std::move将其转换为右值引用返回,确保原右值的所有权正确传递。
  3. 单函数的通用性:通过decltype推导返回值类型,避免了重载带来的代码冗余,同时保持了逻辑的一致性。

内容的提问来源于stack exchange,提问作者Nikki Chumakov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 22:45:29