> 文章列表 > 左值右值完美转发移动语义

左值右值完美转发移动语义

左值右值完美转发移动语义

一.左值和右值

有名字的对象都是左值,右值没有名字;

能对表达式取地址,就是左值;否则为右值;

C++11扩展了右值的概念,将右值分为了纯右值和将亡值;

纯右值:a)非引用返回的临时变量;b)运算表达式产生的结果c)字面常量(c风格的字符串除外,它是地址)

将亡值;与右值引用相关的表达式,例如:将要被移动的对象,T&&函数返回的值,std::move的返回值,转换成T&&的类型的转换函数的返回值

int&& a = 3;//a是左值,3是右值
int b= 8;
int&& c = b+5;
int&& d = c;//c是右值引用,但是是一个左值,讲左值绑定到右值上出错

引入右值引用的目的主要是实现移动语义;

常量左值引用可以绑定任何类型,是一个万能的引用类型。可以绑定非常量左值,常量左值,右值;

二.移动语义

深拷贝将对象中的堆区资源复制了一份,造成资源的申请和释放,如果直接使用源对象使用的资源,可以节省资源申请和浪费的时间。

移动构造函数:

        className(className&& obj)

移动赋值函数:

        className& operator=(className&& obj)

#include <iostream>
#include <string>class Digit
{
public:Digit() = default;Digit(const Digit& obj){std::cout << "拷贝构造" << std::endl;if (m_ptr == nullptr) alloc();memcpy(m_ptr, obj.m_ptr, sizeof(int));}Digit& operator=(const Digit& obj){std::cout << "赋值构造" << std::endl;if (this == &obj) return *this;if (m_ptr == nullptr) alloc();memcpy(m_ptr, obj.m_ptr, sizeof(int));return *this;}Digit(Digit&& obj){std::cout << "移动构造" << std::endl;if (m_ptr != nullptr){delete m_ptr;m_ptr = nullptr;}m_ptr = obj.m_ptr;obj.m_ptr = nullptr;}Digit& operator=(Digit&& obj){std::cout << "移动赋值构造" << std::endl;if (this == &obj) return *this;if (m_ptr != nullptr){delete m_ptr;m_ptr = nullptr;}m_ptr = obj.m_ptr;obj.m_ptr = nullptr;return *this;}~Digit(){if (m_ptr != nullptr){delete m_ptr;m_ptr = nullptr;}}void alloc(){m_ptr = new int;memset(m_ptr, 0, sizeof(int));}int* m_ptr = nullptr;
};int main()
{Digit obj1;obj1.alloc();*obj1.m_ptr = 8;Digit obj3(obj1);auto f = []() {Digit aa; aa.alloc(); *aa.m_ptr = 7; return aa;};Digit obj2 = f();return 0;
}

使用std::move将左值转为右值。std::move是为了告诉编译器,不要对我使用拷贝构造函数,要用移动构造函数

三.完美转发

在函数模板中,可以将参数完美的转发给其他函数,不仅仅保持参数的值,而且保证参数的左,右值属性不变。

为了支持完美转发,C++11提供了以下方案:

1)  如果模板中的参数写成T&&,既可以接收左值引用,又可以接受右值引用

2) 提供了模板函数std::forward<T>(参数),用于转发参数

void func1(int&& i)
{std::cout << "右值" << std::endl;
}void func1(int& i)
{std::cout << "左值" << std::endl;
}template<typename T>
void func(T&& arg)
{func1(std::forward<T>(arg));
}int main()
{int k = 65;func(k); func(2);return 0;
}