左值:能对表达式取地址、或具名对象/变量。一般指表达式结束后依然存在的持久对象。
右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不再存在的临时对象。
左值引用:绑定到左值的引用,通过&来获得左值引用。非常量左值引用只能绑定到非常量左值上;常量左值引用可以绑定到非常量左值、常量左值、非常量右值、常量右值等所有的值类型。
右值引用:C++11中新增加的一个很重要的特性,他主是要用来解决C++98/03中遇到的两个问题,第一个问题就是临时对象非必要的昂贵的拷贝操作,第二个问题是在模板函数中如何按照参数的实际类型进行转发。通过引入右值引用,很好的解决了这两个问题,改进了程序性能.
从4行代码看右值引用 - qicosmos(江南) - 博客园从4行代码看右值引用 概述 右值引用的概念有些读者可能会感到陌生,其实他和C++98/03中的左值引用有些类似,例如,c++98/03中的左值引用是这样的: int i = 0; int& jhttps://www.cnblogs.com/qicosmos/p/4283455.html
T&& k = getVar();
getVar()产生的临时值在表达式结束之后不会被自动销毁,他的生命周期将会通过右值引用得以延续,和变量k的生命周期一样长。
void processValue(int& a) { cout << "lvalue" << endl; }
void processValue(int&& a) { cout << "rvalue" << endl; }
void processValue(const int& a) { cout << "const lvalue" << endl; }
template
void forwardValue(T& val)
{
processValue(val); //右值参数会变成左值
}
template
void forwardValue(const T& val)
{
processValue(val); //参数都变成常量左值引用了
}
int main()
{
int i = 0;
forwardValue(i); //传入左值
forwardValue(0);//传入右值
return 0;
}
输出:
lvalue
const lvalue
void processValue(int& a){ cout << "lvalue" << endl; }
void processValue(int&& a){ cout << "rvalue" << endl; }
template
void forwardValue(T&& val)
{
processValue(val);
}
void Testdelcl()
{
int i = 0;
forwardValue(i); //传入左值
forwardValue(0);//传入右值
int&& t = 0;
processValue(t);
processValue(0);
}
输出:
lvaue
lvalue 因为T&& val是一个右值引用,但是val本身是个左值,因此调用的是lvalue函数。
lvalue 因为t是一个右值引用,但是t本身是个左值,传递给processValue的是一个左值引用
因此调用的是lvalue函数。
rvalue 传递给processValue的是一个右值引用
std::forward:C++11引入了完美转发:在函数模板中,完全依照模板的参数的类型(即保持参数的左值、右值特征),将参数传递给函数模板中调用的另外一个函数。C++11中的std::forward正是做这个事情的,他会按照参数的实际类型进行转发。
void processValue(int& a){ cout << "lvalue" << endl; }
void processValue(int&& a){ cout << "rvalue" << endl; }
template
void forwardValue(T&& val)
{
processValue(std::forward(val)); //照参数本来的类型进行转发。
}
void Testdelcl()
{
int i = 0;
forwardValue(i); //传入左值
forwardValue(0);//传入右值
}
输出:
lvaue
rvalue
universal references:右值引用T&&是一个universal references,可以接受左值或者右值,正是这个特性让他适合作为一个参数的路由,然后再通过std::forward按照参数的实际类型去匹配对应的重载函数,最终实现完美转发。
当T为左值引用,类似(int&),(int& &&)根据引用折叠,等于(int&)(左值引用);
当T为右值引用,类似(int&&),(int&& &&)根据引用折叠,等于(int&&)(右值引用)
引用折叠
std::move
//原始的,最通用的版本
template
struct remove_reference
{
typedef T type;
};
//部分版本特例化,将用于左值引用和右值引用
template struct remove_reference //左值引用
{
typedef T type;
};
template struct remove_reference //右值引用
{
typedef T type;
};
int main()
{
int a = 0;
remove_reference::type t1; //通用版本
remove_reference::type t2; //通用版本
int& b = a;
remove_reference::type t3; //左值引用版本
int&& c = 0;
remove_reference::type t4; //右值引用版本
return 0;
}
template
typename remove_reference::type&& move(T&& t)
{
return static_cast::type &&>(t);
};
int var = 10;
转化过程:
1. std::move(var) => std::move(int&& &) => 折叠后 std::move(int&)
2. 此时:T 的类型为 int&,typename remove_reference::type 为 int
,这里使用 remove_reference 的左值引用的特例化版本
3. 通过 static_cast 将 int& 强制转换为 int&&
整个std::move被实例化如下
int&& move(int& t)
{
return static_cast(t);
}
Tips:
push_back中代表的是右值,emplace_back代表的是universal reference。
emplace_back的执行效率比push_back要高,emplace_back会少调用一次拷贝或者移动构造函数,但是前提是他俩不能混着用。