c++左值,左值引用,右值,右值引用理解

首先看一个函数声明:
void func(type &&rval);
其中 type可以是任意类型(并非函数模板参数)。需要注意的是,此函数只能接收一种实参“类型”:右值,注意此类型指的是左值,左值引用,右值,右值引用这四种之一。

以下我们暂时假定typestd::string
这种规定意味着我们只能通过这样:
func(std::string("hello"));//手动构造一个右值传入
或者这样:

std::string s("hello");
func(std::move(s));//使用std::move()强转为右值,注意,是强转为右值!!!

以及这样:

std::string s("world");
func(static_cast<string &&>(s));		

来调用此函数。

可能有人会产生疑问:既然func函数函数形参是std::string&&类型,那么我传入一个相应的右值引用类型不可以吗?
即:

std::string &&rs = std::string("hello world");
func(ss);//error

这似乎与我们的直觉相违背,错误产生的原因有两点:

  • 右值引用本身是左值
  • 右值引用只能绑定到右值
    至于为什么,因为标准就是这么规定的。。。。

而与右值引用相对应的左值引用就没有太多限制,当我们声明一个具有左值引用形参的函数时:
void fun(std::string &s);

//显然其可以接受一个左值:
std::string s_("hehe");
fun(s_);
//同时也可以接受一个左值引用:


fun(ls);
//甚至可以绑定到一个右值引用:
std::string && rs = std::("haha");
fun(rs);

当然,当函数声明为void fun(const std::string &s)时,亦可以传入一个右值给此函数:

fun(std::string("fuck"));//ok

我们知道,函数传参规则和变量初始化规则相同,所以上述三种情况可以写成:

std::string s("fucccccck");
std::string &lval_1 = s;//左值引用绑定到左值
std::string &lval_2 = lval_1;//左值引用绑定到同类型引用所绑定的左值上
std::string && rval = std::string("ff");
std::string &lval_3 = rval;//情况3

这里情况3将一个右值引用初始化给一个左值引用,那lval_3到底绑定到谁呢?答案是左值引用会绑定到右值引用所绑定的右值对象上

我们知道非const引用不能绑定到const对象上:

std::string &lref = std::string("haha");//error

但是以下方式却能间接绑定到右值:

std::string &&rref = std::string("hehehe");
std::string &lref = rref;
std::cout << lref << std::endl;//"hehehe"
lref[0] = 'p';
std::cout << lref << std::endl;//"pehehe"

所以对于初始化一个左值引用,不管是用一个左值,还是一个同类型的左值引用,或者一个右值引用,最终这个左值引用都会绑定到一个对象上。


接下来看一下std::move()和std::forward()函数。

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type &&>(t);
}

让我比较产生疑惑的是move()函数的返回值,当我们传一个左值给move():

std::string s("he");
std::move(s);

函数模板move()的类型参数T会被推断为std::string &,即move()函数会变成这个样子:

typename remove_reference<std::string &>::type&& move(std::string& &&t)
{
	return static_cast<typename remove_reference<std::string &>::type &&>(t);
}

typename remove_reference<T>::type的作用就是将类型T中的引用去除,还原出原生类型。再通过引用折叠,所以最终move()函数会被实例化为下面这样:

std::string&& move(std::string &t)
{
	return static_cast<std::string &&>(t);
}

看似move()函数返回一个右值引用类型,但其实返回的是一个右值

未完待续

猜你喜欢

转载自blog.csdn.net/qq_39815320/article/details/109298728
今日推荐