C++20:如何判断模板参数为右值引用并实现条件移动拷贝
问题分析与解决
核心问题:万能引用的类型推导误解
你写的T&& v是万能引用(转发引用),类型推导规则和普通引用不同:
- 传入左值(比如直接传
a)时,T会被推导为std::vector<std::string>&,此时T&&折叠为左值引用; - 传入右值(比如
std::move(a))时,T会被推导为std::vector<std::string>(非引用类型),此时T&&是右值引用。
原代码中std::is_rvalue_reference_v<T>判断的是T本身的类型,而非v的类型——传入右值时T是非引用类型,自然返回false,导致永远走拷贝分支。
修正方案
1. 修正判断条件
我们需要判断v的类型是否为右值引用,或通过T的类型间接判断:
- 方案一:用
std::is_rvalue_reference_v<decltype(v)>直接检查v的类型; - 方案二:用
!std::is_lvalue_reference_v<T>——传入右值时T是非引用类型,该条件为true;传入左值时T是左值引用,条件为false。
2. 正确移动元素
遍历右值引用的容器时,迭代器返回的元素仍是左值引用,必须用std::move()将其转为右值引用,才能触发移动构造,让原字符串的size变为0。
修正后的代码
#include <vector> #include <string> #include <iostream> #include <type_traits> template<class T> void func(T&& v){ for (auto&& item : v) { if constexpr (!std::is_lvalue_reference_v<T>) { // 传入右值时触发移动分支 auto tmp = std::move(item); } else { auto tmp = item; } } } int main() { std::vector<std::string> a {"11", "22"}; func(std::move(a)); std::cout << a[0].size(); // 现在输出0 return 0; }
补充说明:关于std::forward的使用
你之前尝试的auto tmp = std::forward<decltype(item)>(item)无效,是因为item是容器元素的左值引用(即使v是右值引用),decltype(item)为std::string&,std::forward会保留左值引用属性,因此还是执行拷贝。只有明确用std::move(item)将其转为右值引用,才能触发移动操作。
内容的提问来源于stack exchange,提问作者user3324131
相关产品推荐
相关产品推荐

