左值和右值
左值和右值判断:
1)可位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值。
2)有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。
例如:
int main()
{
//左值右值
int a = 10;
const int b = 10;
//10 = a; 右值不能被赋值
//&表示左值引用,普通左值引用只能接收左值,不能接收右值,但是常量左值引用既可以接收左值也可以接收右值。
int c = 10;
int &d = c;
//int &e = 10;//错误
const int &f = 10;
const int &g = f;
//两个&表示右值引用,常规右值引用可以用来接收右值,但是不能用来接收左值
int h = 10;
int &&i = 10;
//int &&j = i;//错误
int &&k = std::move(i);// std::move() 函数,它可以将左值强制转换成对应的右值,
getchar();
return 0;
}
移动构造函数
class Test
{
public:
Test():data(new int(0)) {
cout << "construct" << endl; };
Test(const Test & parm):data(new int(*parm.data)){
cout << "copy construct" << endl; }
Test(Test &&parm) :data(parm.data)
{
parm.data = NULL;
cout << "move construct" << endl;
}
Test& operator= (const Test &parm)
{
cout << "assignment construct" << endl;
if (this == &parm)
return *this;
if (data)
delete data;
data = new int (*parm.data);
return *this;
}
~Test() {
cout << "destory" << endl; if(data)delete data; };
public:
int * data = NULL;
};
int main()
{
{
Test a;
cout << "a" << *a.data << endl;
Test b = std::move(a);//慎用
//将a强行转为右值后,进行移动构造,a的资源会被转移
//Test c = a; 调用复制构造会崩溃,a已经没有资源了
if(a.data)
cout << "a" << *a.data << endl;
cout << "a.data 不存在" << endl;
Test c = b;
Test d;
d = c;
}
getchar();
return 0;
}
提到了移动构造,就要提完美转发
完美转发(perfect forwarding)问题是指函数模板在向其他函数转发(传递)自身参数(形参)时该如何保留该参数(实参)的左右值属性的问题。也就是说函数模板在向其他函数转发(传递)自身形参时,如果相应实参是左值,它就应该被转发为左值;同样如果相应实参是右值,它就应该被转发为右值。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动语义)的可能性。如果将自身参数不分左右值一律转发为左值,其他函数就只能将转发而来的参数视为左值,从而失去针对该参数的左右值属性进行不同处理的可能性。
首先提一下引用折叠
我们把 引用折叠 拆解为 引用和 折叠 两个短语来解释。
首先,引用的意思众所周知,当我们使用某个对象的别名的时候就好像直接使用了该对象,这也就是引用的含义。在C++11中,新加入了右值的概念。所以引用的类型就有两种形式:左值引用T&和右值引用T&&。
其次,解释一下折叠的含义。所谓的折叠,就是多个的意思。上面介绍引用分为左值引用和右值引用两种,那么将这两种类型进行排列组合,就有四种情况:
左值-左值 T& &
左值-右值 T& &&
右值-左值 T&& &
右值-右值 T&& &&
所有的引用折叠最终都代表一个引用,要么是左值引用,要么是右值引用。
规则就是:
如果任一引用为左值引用,则结果为左值引用。否则(即两个都是右值引用),结果为右值引用。如下
函数参数声明的类型 传入实参的类型 r的类型
T& TR r&
T& TR& r&
T& TR&& r&
T&& TR r&&
T&& TR& r&
T&& TR&& r&&
测试用例
#include <iostream>
#include <stdlib.h>
#include <type_traits>
void func(int& t)
{
std::cout << "func(int& t)" << std::endl;
}
void func(const int& t)
{
std::cout << "func(const int& t)" << std::endl;
}
void func(const int&& t)
{
std::cout << "func(const int&& t)" << std::endl;
}
template<typename T>
T&& forward(T ¶m)//虽然简陋,但是std::forward大抵就是做这些事
{
return static_cast<T&&>(param);
}
template<typename T>
void myFun(T&& t)
{
//func(forward<T>(t)); //自定义forward
//func(std::forward<T>(t));//标准库forward
func(t);
}
int main()
{
int a = 0;
myFun(a);
const int b = 20;
myFun(b);
myFun(std::move(a));
getchar();
return 0;
}
直接执行
去掉注释,使用完美转发后
关于完美转发,认为了解即可