当前位置:网站首页>(P40-P41)move资源的转移、forward完美转发
(P40-P41)move资源的转移、forward完美转发
2022-06-12 08:01:00 【喜欢打篮球的普通人】
1.move
在 C++11 添加了右值引用,并且不能使用左值初始化右值引用,如果想要使用左值初始化一个右值引用需要借助 std::move () 函数,使用std::move方法可以将左值转换为右值。(可以将左值、左值引用、右值引用转换为右值(STL源码返回的是一个将亡值T&&))
使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。
从实现上讲,std::move 基本等同于一个类型转换:static_cast<T&&>(lvalue);,函数原型如下:
template<class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) _NOEXCEPT
{
// forward _Arg as movable
return (static_cast<remove_reference_t<_Ty>&&>(_Arg));
}
- eg:
class Test
{
public:
Test(){
}
......
}
int main()
{
Test t;
//使用左值初始化右值引用,因此语法是错误的
Test && v1 = t; // error
//使用 move() 函数将左值转换为了右值,这样就可以初始化右值引用了。
Test && v2 = move(t); // ok
return 0;
}
- eg:假设一个临时容器很大,并且需要将这个容器赋值给另一个容器,就可以执行如下操作:
list<string> ls;
ls.push_back("hello");
ls.push_back("world");
......
list<string> ls1 = ls; // 需要拷贝, 效率低
list<string> ls2 = move(ls);
- 解释:
如果不使用 std::move,拷贝的代价很大,性能较低。使用 move 几乎没有任何代价,只是转换了资源的所有权。如果一个对象内部有较大的堆内存或者动态数组时,使用 move () 就可以非常方便的进行数据所有权的转移。
另外,也可以给类编写相应的移动构造函数(T::T(T&& another)),和具有移动语义的赋值函数(T&& T::operator=(T&& rhs)),在构造对象和赋值的时候尽可能的进行资源的重复利用,因为它们都是接收一个右值引用参数。
2.forward
右值引用类型是独立于值的,一个右值引用作为函数参数的形参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值(左值引用更好理解些?),并不是原来的类型了。
如果需要按照参数原来的类型转发到另一个函数,可以使用 C++11 提供的 std::forward () 函数,该函数实现的功能称之为完美转发。
- 函数原型
// 函数原型
template <class T> T&& forward (typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& t) noexcept;
// 精简之后的样子
std::forward<T>(t);
- 用法:
当T为左值引用类型时,t将被转换为T类型的左值
当T不是左值引用类型时,t将被转换为T类型的右值
- eg:
#include <iostream>
using namespace std;
template<typename T>
void printValue(T& t)
{
cout << "l-value: " << t << endl;
}
template<typename T>
void printValue(T&& t)
{
cout << "r-value: " << t << endl;
}
template<typename T>
void testForward(T&& v)
{
printValue(v);
printValue(move(v));
printValue(forward<T>(v));
cout << endl;
}
int main()
{
/* testForward(520); 函数的形参为未定引用类型 T&&,实参为右值,初始化后被推导为一个右值引用 printValue(v); 已命名的右值 v,编译器会视为左值处理,实参为左值(准确说是左值引用) printValue(move(v)); 已命名的右值编译器会视为左值处理,通过 move 又将其转换为右值,实参为右值 printValue(forward<T>(v));forward 的模板参数为右值引用,最终得到一个右值,实参为 ``右值` */
testForward(520);
int num = 1314;
/* testForward(num); 函数的形参为未定引用类型 T&&,实参为左值,初始化后被推导为一个左值引用 printValue(v); 实参为左值(住准确说是左值引用) printValue(move(v)); 通过 move 将左值转换为右值,实参为右值 printValue(forward<T>(v));forward 的模板参数为左值引用,最终得到一个左值引用,实参为左值 */
testForward(num);
/* testForward(forward<int>(num));forward 的模板类型为 int,最终会得到一个右值,函数的形参为未定引用类型 T&& 被右值初始化后得到一个右值引用类型 printValue(v); 已命名的右值 v,编译器会视为左值处理(准确说是左值引用),实参为左值 printValue(move(v)); 已命名的右值编译器会视为左值处理,通过 move 又将其转换为右值,实参为右值 printValue(forward<T>(v));forward 的模板参数为右值引用,最终得到一个右值,实参为右值 */
testForward(forward<int>(num));
/* testForward(forward<int&>(num));forward 的模板类型为 int&,最终会得到一个左值,函数的形参为未定引用类型 T&& 被左值初始化后得到一个左值引用类型 printValue(v); 实参为左值(准确说是左值引用) printValue(move(v)); 通过 move 将左值转换为右值,实参为右值 printValue(forward<T>(v));forward 的模板参数为左值引用,最终得到一个左值,实参为左值 */
testForward(forward<int&>(num));
/* testForward(forward<int&&>(num));forward 的模板类型为 int&&,最终会得到一个右值,函数的形参为未定引用类型 T&& 被右值初始化后得到一个右值引用类型 printValue(v); 已命名的右值 v,编译器会视为左值处理(准确说是左值引用),实参为左值 printValue(move(v)); 已命名的右值编译器会视为左值处理,通过 move 又将其转换为右值,实参为右值 printValue(forward<T>(v));forward 的模板参数为右值引用,最终得到一个右值,实参为右值 */
testForward(forward<int&&>(num));
return 0;
}
- 测试:

#include <iostream>
using namespace std;
template<typename T>
void printValue(T& t)
{
cout << "l-value: " << t << endl;
}
//&& 是未定义类型的引用
template<typename T>
void printValue(T&& t)
{
cout << "r-value: " << t << endl;
}
//&& 是未定义类型的引用
template<typename T>
void testForward(T && v)
{
printValue(v);
printValue(move(v));
printValue(forward<T>(v));
cout << endl;
}
int main()
{
testForward(520);
int num = 1314;
testForward(num);
testForward(forward<int>(num));
testForward(forward<int&>(num));
testForward(forward<int&&>(num));
return 0;
}
- 参考:转移和完美转发
边栏推荐
- Multithread decompression of tar
- Rich dad, poor dad Abstract
- 20220524 深度学习技术点
- . net mysql Too many connections
- Literature reading: raise a child in large language model: rewards effective and generalizable fine tuning
- Solve mapper duplication problem in reverse engineering
- Talk about the four basic concepts of database system
- S-msckf/msckf-vio technical route and code details online blog summary
- Leetcode notes: biweekly contest 69
- Conda創建虛擬環境報錯,問題解决
猜你喜欢

Database connection pool and dbutils tool

Symfony 2: multiple and dynamic database connections

Multithread decompression of tar

Derivation of Poisson distribution

Process terminated

Topic 1 Single_ Cell_ analysis(3)

Topic 1 Single_ Cell_ analysis(1)

Alibaba cloud deploys VMware and reports an error

Vision Transformer | Arxiv 2205 - LiTv2: Fast Vision Transformers with HiLo Attention

Search and rescue strategy of underwater robot (FISH)
随机推荐
Mathematical Essays: Notes on the angle between vectors in high dimensional space
OpenMP task 原理與實例
Conda創建虛擬環境報錯,問題解决
Process terminated
Procedure execution failed 1449 exception
Process terminated
Interview questions on mobile terminal, Android and IOS compatibility
Topic 1 Single_ Cell_ analysis(3)
MSE (mean square error) calculation package function
Summary of structured slam ideas and research process
20220525 RCNN--->Faster RCNN
Leetcode notes: biweekly contest 71
2021.10.29-30 scientific research log
OpenMP task 原理与实例
Xiaomi mobile phone recording data set software operation
802.11 protocol: wireless LAN protocol
Compiling principle on computer -- functional drawing language (V): compiler and interpreter
2.2 linked list - Design linked list (leetcode 707)
2.1 链表——移除链表元素(Leetcode 203)
Visual studio code batch comment and uncomment