如何用单个函数实现兼具完美转发与拷贝消除的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; } }
关键说明
- 类型匹配触发拷贝消除:当临时变量
tmp的类型与函数返回值类型完全一致时,return tmp满足返回值优化(RVO)的条件——编译器可以直接将tmp构造到调用方的返回值存储位置,避免拷贝或移动操作。 - 右值引用的正确转发:对于右值引用类型的
tmp,需要用std::move将其转换为右值引用返回,确保原右值的所有权正确传递。 - 单函数的通用性:通过
decltype推导返回值类型,避免了重载带来的代码冗余,同时保持了逻辑的一致性。
内容的提问来源于stack exchange,提问作者Nikki Chumakov
相关产品推荐
相关产品推荐

