1. 何为左值?何为右值?
简单的说,左值可以放在等号的左边,右值可以放在等号的右边。
左值可以取地址,右值不能取地址。
1.1 左值举例:
- 变量、函数或数据成员
- 返回左值引用的表达式 如 ++x、x = 1、cout << ' ' int x = 0
1.2 右值举例:
- 返回非引用类型的表达式
如 x++、x + 1 - 除字符串字面量之外的字面量如 42、true
将亡值(xvalue)
- 隐式或显式调用函数的结果,该函数的返回类型是对所返回对象类型的右值引用
#include<string>
#include<iostream>
using namespace std;
void print(string& str)
{
cout << "left val: " <<str << endl << endl;
}
void print(string&& str)
{
cout << "right val: " << str <<endl << endl;
}
void print(int&& i)
{
cout << "right val: " <<i << endl << endl;
}
void print(int& i)
{
cout << "left val: " <<i << endl << endl;
}
int getVal(){
return 666;
}
int main(int argc, char *argv[])
{
string str1 = "hello 1";
print(str1);
print("hello 2");
print(move(str1));
int ab = 88;
print(ab);
print(++ab);
print(ab++);
print(getVal());
//string& aa = "abc"; // 编译不过,左值引用不可以赋右值
const string& bb = "abc"; // 常量左值引用可以赋右值
return 0;
}
输出:
1.3 左值引用与右值引用
规则简化如下:
左值引用 {左值}
右值引用 {右值}
常左值引用 {右值}
string f()
{
return "bbb";
}
int main()
{
string &s1 = "asd"; // error
const string &s2 = "asd"; // ok
const string& s3 = f(); // ok
string&& a2 = "defg"; // ok
string&& a3 = f(); // ok
return 0;
}
1.4 引入右值引用意义
可以延长右值的生命周期,右值的生命周期可以与右值引用变量相同
2. 完美转发
2.1 转发引用
在 T 是模板参数时,T&& 的作用主要是保持值类别进行转发,它有个名字就叫“转发引用”(forwarding reference)。因为既可以是左值引用,也可以是右值引用,它也曾经被叫做“万能引用”(universal reference)。
#include<string>
#include<iostream>
using namespace std;
void print(const string& str)
{
cout << "left val: " <<str << endl << endl;
}
void print(string&& str)
{
cout << "right val: " << str <<endl << endl;
}
string ff()
{
return "fff";
}
template<typename T>
void f(T&& param)
{
print(forward<T>(param));
}
int main(int argc, char *argv[])
{
string str1 = "hello 1";
const string& bb = "abc";
const string& bb2 = ff();
f(str1); // left value
f(bb); // left value
f(bb2); // left value
f("abcd"); // rightvalue
f(ff()); // right value
return 0;
}
输出:
2.2 完美转发 std::forward
2.1.1 源码解析
- 转发左值
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
先通过 remove_reference 获得类型type,定义
_t
为左值引用的左值变量,通过static_cast
进行强制转换
- 转发右值
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
不同于转发左值,
_t
为右值引用的左值变量,除此之外中间加了一个断言,表示当不是左值的时候,也就是右值,才进行static_cast
转换。
2.1.2 remove_reference 解析
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
// 特化版本
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
remove_reference
的作用是去除T
中的引用部分,只获取其中的类型部分。无论T
是左值还是右值,最后只获取它的类型部分。文章来源:https://www.toymoban.com/news/detail-629737.html
现代C++之万能引用、完美转发、引用折叠 - 知乎 (zhihu.com)文章来源地址https://www.toymoban.com/news/detail-629737.html
到了这里,关于C++ 学习系列 1 -- 左值、右值与万能引用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!