c++ 左值引用 右值引用 统一引用 及 参数引用 & 与&&

先了解引用的作用:C++引用的作用_Lao_tan的博客-CSDN博客_c++引用的作用
右值引用_百度百科

C++中rvalue和lvalue详悉_Naruto_Q的博客-CSDN博客_lvalue rvalue

lvalue(left value)、rvalue(right value)是c语言编译过程中的用于描述等号左右值的符号。但是不能直观的通过在等号左边还是右边来判断是lvalue还是rvalue。能够取地址的,有名字的就是左值;反之,不能取地址,没有名字的就是右值。
左值(lvalue)可以是明确的变量。右值(rvalue)可以是临时变量,比如表达式、函数返回值、常量等。
int a = 1;
int b;

//下面为左值引用
int &aa0 = a;//ture
int &bb0 = b;//ture
int &aa1 =1;//error
int &aa1 = a+1;//error

//下面为右值引用

int &&a1 = a;//error
int &&b1 = b;//error
int &&a2  = 1;//ture
int &&a3 = a+1;//ture
int &&a4 = std::move(a);//ture  ,std::move()强制将左值转换成右值

//可以通过这个函数来判断是lvalue还是rvalue。
bool is_r_value(int &&)
{
    return true;
}
bool is_r_value(const int &)
{
    return false;
}


可以将C++的引用理解为指针常量引用必须在声明时初始化,声明之外的赋值操作都是对引用所代指地址的内容访问。
C++参数中的& 和&& 都表示引用。左值的引用(lvalue)声明符号为”&”, 为了和左值区分,右值的引用(rvalue)声明符号为”&&”。 右值引用变量初始化之后引用本身变成了lvalue。所以无法进行“&&=&&”,从而达到禁止右值引用隐式传递的目的;但是左值引用可以传递,所以可以进行“&=&”。引用初始化必须满足lvalue = lvalue或者rvalue = rvalue。“const type &” 或“type const &”(比如const int &)被称为万能引用,不管是值、指针、左引用还是右引用,它都能“照单全收”。
不同编辑器表现可能会有差异。比如vs中对类或结构体操作时不一定非要满足lvalue = lvalue或者rvalue = rvalue的原则。

static int t = 0;
class Whore {
public:
    char buf[10];
    Whore()
    {
        itoa(t++,buf,10);
        cout << "Whore():" << buf<< endl;
    }
    ~Whore()
    {

        cout << "~Whore():"<<buf << endl;
    }
}

void fun(int &a)
{
	cout << "fun(int &a)" << endl;
}

void fun(int &&a)//&&用于存放右值(可以是表达式、临时变量等等)的引用
{
	cout << "fun(int &&a)" << endl;
}

void fun1(Whore &a)
{
	cout << "fun1(Whore &a)" << endl;
}
void fun1(Whore &&a)
{
	cout << "fun1(Whore &&a)" << endl;
}

void fun2(Whore &a)
{
	cout << "fun2(Whore &a)" << endl;
}

void fun3(int &a)
{
	cout << "fun3(int &a)" << endl;
}

int main()
{
	int a = 0;
	Whore w;
	fun(a);
	fun(1); //1为匿名临时变量
	fun1(w);
	fun1(Whore());//Whore()为匿名临时变量,生命周期为fun1(Whore && a)的函数栈。
	//fun2(Whore()); //vs编辑器不报错,编译不报错,可执行;qt编辑器报错:(no matching function for call to 'fun2'),不可执行
	//fun3(1);  //vs编辑器报错:(非常量引用的初始值必须为左值),编译报错:(无法将参数 1 从“int”转换为“int &”),不可执行;qt编辑器报错:(no matching function for call to 'fun3'),不可执行
    cout<<"end"<<endl;

    int &a0 = a;用变量对左值引用进行初始化,等价于lvalue = lvalue。
    a0 = 2;
    //int &a1 = 1;//vs编辑器报错:(非常量引用的初始值必须为左值),编译器报错:(无法从“int”转换为“int &”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &”)
    //int &a1 = a+1;//vs编辑器报错:(非常量引用的初始值必须为左值),编译器报错:(无法从“int”转换为“int &”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &”)
    int &a1 = a0;//用左值引用对左值引用进行初始化,等价于lvalue = lvalue。
    int *pa = &a0;
    const int &a2 = 1;//该语句在qt和vs中的汇编语言等价于“int &&a2 = 1;”,但是该语句a2指向的地址的内容无法修改。const int & 称为万能引用,不管是值、指针、左引用还是右引用,它都能“照单全收”。
    const int &a2_1 = *pa;//右值引用(表达式)
    const int &a2_2 = a0;//左值引用
    const int &a2_3 = a0+1;//右值引用(表达式)
    int &&a3 = 1;//用常数对右值引用进行初始化,等价于rvalue = rvalue。
    a3 = 2;
    const int &&a4 = 1;
    //int &&a5 = a;//用变量对右值引用初始化,等价于rvalue = lvalue,失败。//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
    //int &&a6 = a0;//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(rvalue reference to type 'int' cannot bind to lvalue of type 'int'),qt编译器报错:(无法从“int”转换为“int &&”)
    int &&a7 = a+0;//用表达式对右值引用进行初始化,等价于rvalue  = rvalue。
    int &&a8 = a0+0;//用表达式对右值引用进行初始化,等价于rvalue = rvalue。
    //int &&a9 = a3;//用已经初始化的右值引用对右值引用初始化,然而引用初始化之后引用本身变成了lvalue,等式等价于rvalue = lvalue,所以无效。//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
    int &&a10 = a3+0;//用表达式对右值引用进行初始化,等价于rvalue = rvalue。
    //const int &&a11 = a0;//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“const int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'const int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
    int &a12 = a3;//用已经初始化的右值引用对左值引用初始化,相当于进行lvalue = lvalue操作。
    fun(a);
    fun(a0);
    fun(a3);
    //fun(a4);//vs编辑器报错:(没有与参数列表匹配的 重载函数 "fun" 实例),vs编译器报错:(“fun”: 2 个重载中没有一个可以转换所有参数类型);qt编辑器报错:( no matching function for call to 'fun'),编译器报错:(“fun”: 2 个重载中没有一个可以转换所有参数类型)
    fun(1+1);
    fun(a+1);
    fun(a0+0);
    fun(a3+1);
	return 0;
}

/*输出
Whore():0
fun(int &a)
fun(int &&a)
fun1(Whore &a)
Whore():1
fun1(Whore &&a)
~Whore():1
end
fun(int &a)
fun(int &a)
fun(int &a)
fun(int &&a)
fun(int &&a)
fun(int &&a)
fun(int &&a)
~Whore():0
*/

const int *  ,const int *& , int * const , int * const &。无法将int * 转换成const int *&。
无法从“int *”转换为“int *&”?详解C++引用(&)使用方法_代码乌龟的博客-CSDN博客_无法从int?转换为int

    int d = 10;
	int *p = &d;    
    const int * p1 = &d;
    int d1 = 20;
    const int * p2 = &d1;
    typedef int * IntT;
	typedef int const * IntTC;
	const IntT &pi = p;//const IntT 等价于IntT const ,等价于int * const
	IntT const &pi1 = p;
	int * &pi2 = p;
	int * const &pi3 = p;
	//const int * &pi4 = p;//vs编辑器报错:无法用 "int *" 类型的值初始化 "const int *&" 类型的引用(非常量限定)	;vs编译器报错: 无法从“int *”转换为“const int *&”
	//IntTC &pi5 = p;//同上
    const int *&pi6 = p1;
	//pi4 = a;//error,vs编辑器报错:表达式必须是可修改的左值
	*pi = 1;
	//pi = a;//vs编辑器报错:表达式必须是可修改的左值
	*pi2 = 1;
	pi2 = a;
	*pi2 = 1;
	//pi2 = a;//error,vs编辑器报错:表达式必须是可修改的左值
    pi6 = p2;
    //*pi6 = 11;//error,vs编辑器报错:表达式必须是可修改的左值

C++临时变量的生命周期_snail0428的博客-CSDN博客_临时变量生命周期 
【C++】拷贝构造函数的调用时机_翼同学的博客-CSDN博客_c++拷贝构造函数什么时候调用 
返回引用的意义_陈小淘的博客-CSDN博客_函数返回引用的含义 
C++将返回值为引用有什么作用? - 知乎


(右值)引用可以改变临时变量的生命周期!!!一般而言,函数返回类型不能返回右值引用:T &&,返回右值引用在逻辑上不成立,且会造成非法越界访问。返回的左值引用也可能会造成越界访问,需要理解清楚左值引用所引用对象的生命周期。std::forward<T>(var)与std::move(var)两者分别用于对变量var进行转移语义和转换语义,目的都是改变参数的引用类型。

static int t = 0;
class Whore {
public:
	char nickname[10];
	Whore()
	{
		_itoa(t++,buf,10);
		cout << "Whore():"<< nickname << endl;
	}
    Whore(Whore & w)
	{
		char buf[10];
		nickname = _itoa(t++, buf, 10);
		cout << "Whore(Whore & w):" << nickname << endl;
	}
	~Whore()
	{
		cout << "~Whore():"<<nickname << endl;
	}
    Whore& operator =(const Whore & w)
	{
		///char buf[10];
		//nickname = _itoa(t++, buf, 10);
		cout << "Whore& operator =(const Whore & w):" << nickname << ":" << w.nickname << endl;
		strcpy(nickname , w.nickname);
		return *this;
	}
    Whore& operator =(Whore && w)
	{
		///char buf[10];
		//nickname = _itoa(t++, buf, 10);
		cout << "Whore& operator =(Whore && w):" << nickname <<":"<<w.nickname<< endl;
		strcpy(nickname , w.nickname);
		return *this;
	}
}

Whore fun4()
{
    return Whore();
}

Whore fun4_1()
{
    Whore w = Whore();
    return w;
}

Whore & fun5()//错误示范1,返回临时或局部变量的引用,在函数退出后临时变量会被回收,引用也会变的无效,fun5()之外再访问w的成员造成越界。
{
    Whore w = Whore();
	return w;//临时或局部变量在函数调用完后会被释放,外部再使用会导致越界访问。
}

Whore && fun5_1()//错误示范2,返回临时或局部变量的引用,在函数退出后临时变量会被回收,引用也会变的无效,fun5_1()之外再访问w的成员造成越界。
{
	return Whore();//临时或局部变量在函数调用完后会被释放,外部再使用会导致越界访问。
}


Whore & fun6()//错误示范3,左值引用无法改变变量生命周期
{
	Whore wt = Whore();
	static Whore & w = wt;//左值引用无法改变局部变量生命周期。
	return w;
}

Whore & fun6_1()
{
	static Whore & w = Whore();//在vs中能运行成功,在qt中编辑器报错。这里用临时变量初始化左值引用,相当于lvalue = rvalue,正常来讲应该是要报错的。在vs中,这里直接将该临时变量变成全局变量来使用了。
	return w;
}

Whore & fun6_2()
{
    static Whore && w = Whore();//右值引用可以改变临时变量的生命周期。
    return w;
}


Whore fun6_3()
{
    static Whore && w = Whore();//右值引用可以改变临时变量的生命周期。
    return w;
}

Whore fun7()
{
    static Whore w = Whore();
    return w;
}

Whore& fun7_1()
{
    static Whore w = Whore();
    return w;
}

void fun8(Whore & w)
{
	cout << "void fun8(Whore & w)" << endl;
}
void fun8(Whore && w)
{
	cout << "void fun8(Whore && w)" << endl;
}


int main()
{
/*
//下面代码运行成功。注意fun4与fun4_1返回变量与返回临时变量的差异。
    Whore w1 = fun4();//debug 和release下结果不一样,debug中调用构造函数后再调用拷贝构造,release中只调用构造函数。
    cout<<w1.nickname<<endl;
    Whore w2;
    cout<<w2.nickname<<endl;
    w2 = fun4();//调用了赋值重载函数,此时临时变量的生命周期在这条语句结束时结束了。
    cout<<w2.nickname<<endl;
    Whore w3 = fun4_1();//只调用了一次构造函数,等价于Whore ww1 = Whore();此时临时变量的生命周期与ww1等价了。
    cout<<w2.nickname<<endl;
    Whore &w4 = fun4();
    cout<<w4.nickname<<endl;
    Whore &w5 = fun4_1();
    cout<<w5.nickname<<endl;
    Whore &&w6 = fun4();//右值引用拉长了临时变量的生存周期
    cout << w6.nickname << endl;
    Whore &&w7 = fun4_1();//右值引用拉长了临时变量的生存周期
    cout << w7.nickname << endl;
*//*debug输出:
Whore():0
Whore(const Whore &w):1
~Whore():0
1
Whore():2
2
Whore():3
Whore(const Whore &w):4//w2 = fun4();在debug中调用了拷贝构造之后再调用赋值重载函数函数。拷贝构造的作用应该是将fun4中的返回的临时变量先存放到main中一个临时变量值中,然后再main中的临时变量赋值给w2。这个过程设计的内存操作相当的多。在release下有很大的精简。
~Whore():3
Whore& operator =(Whore && w):2:4
~Whore():4
4
Whore():5
4
Whore():6
Whore(const Whore &w):7
~Whore():6
7
Whore():8
8
Whore():9
Whore(const Whore &w):10
~Whore():9
10
Whore():11
11
end
~Whore():11
~Whore():10
~Whore():8
~Whore():7
~Whore():5
~Whore():4
~Whore():1
*//*release 输出: //release 模式下少了很多拷贝构造函数的调用,临时变量在fun4退出后并没有被西沟,意味着都直接延长了临时变量生命周期。在release模式下,右值引用与普通变量基本没有差别
Whore():0
0
Whore():1
1
Whore():2
Whore& operator =(Whore && w):1:2
~Whore():2
2
Whore():3
2
Whore():4
4
Whore():5
5
Whore():6
6
Whore():7
7
end
~Whore():7
~Whore():6
~Whore():5
~Whore():4
~Whore():3
~Whore():2
~Whore():0
*/
/*
//下面代码在qt中失败,在vs中成功。按规则来讲,临时变量是不能赋值给左值引用的,但vs中对类或结构体操作时是可以的。
    Whore &w = fun4();
	cout << w.nickname << endl;
*/

/*
//下面的代码导致崩溃。fun5返回的是临时变量的引用,而该临时变量的生命周期只在“Whore &w = fun5();”语句之中,在main中通过引用再访问变量的内容时,变量已经被销毁了,所以再访问就是越界访问了。
    Whore &w = fun5();
    cout<<w.nickname<<endl;
    Whore &w1 = fun5_1();
    cout<<w.nickname<<endl;
*//*release输出:
Whore():0
~Whore():0//fun5中的临死变量在fun5退出后被析构了,与fun4 的区别在于一个是返回了临时变量或局部,一个是返回了临时或局部变量的引用。
Whore(const Whore &w):1
1
Whore():2
~Whore():2
Whore(const Whore &w):3
3
end
~Whore():3
~Whore():1
*//*debug输出:
Whore():0
~Whore():0
Whore(const Whore &w):1
1
Whore():2
~Whore():2
Whore(const Whore &w):3
3
end
~Whore():3
~Whore():1
*/
/*
//下面代码导致崩溃。fun6中定义了一个全局静态左值引用,并且用一个局部变量初始化左值引用,然而局部变量的生命周期只在fun6的函数栈内,在main中通过fun6返回的引用再访问变量的内容时,变量已经被销毁了,所以再访问就是越界访问了。
    Whore &w = fun6();
	cout << w.nickname << endl;
    Whore &w = fun6_1();
    cout<< w.nickname <<endl;
*/
/*下面代码运行成功:展示了返回引用的一种正确用法,返回引用更多的是用在返回对象成员变量或传入参数的成员变量
//fun6中用临时变量初始化右值引用,是合法的,临时变量生命周期也强制变长了。全局静态变量w初始化之后就成了lvalue了然后以引用的形式进行返回。
    Whore &w = fun6_2(); //整个过程只有一次在fun6_2中调用一次构造函数。返回引用的正确用法,人为避免调用拷贝构造函数
    cout << w.nickname << endl;
*//*debug/release输出:
Whore():0
0
end
~Whore():0
*/
/*下面代码运行成功:都是展示函数返回引用后的错误用法
    Whore w1 = fun6_2();  //函数虽然返回引用,但是依然调用拷贝构造函数。
    cout << w1.nickname << endl;
    //Whore &&w2 = fun6_2()//qt编辑器报错,不能将rvalue绑定到lvalue。
    //Whore &w3 = fun6_3(); //qt编辑器报错,不能将lvalue绑定到临时变量
    Whore w4 = fun6_3();//将fun6_3返回的右值引用赋值给普通变量,会调用拷贝构造函数。注意fun4的区别,fun4不会调用拷贝构造函数。
    cout << w4.nickname << endl;
    Whore && w5 = fun6_3(); //将fun6_3返回的右值引用赋值给右值引用,依然会调用拷贝构造函数。
    cout << w5.nickname << endl;
*//*debug/release输出:
Whore():0
Whore(const Whore &w):1
1
Whore():2
Whore(const Whore &w):3
3
Whore(const Whore &w):4
4
end
~Whore():4
~Whore():3
~Whore():1
~Whore():2
~Whore():0
*/
/*下面代码运行成功:正确用法
    Whore &w = fun7_1();//整个过程只有在fun7_1中一次调用了构造函数。人为避免调用拷贝构造函数
    cout<<w.nickname<<endl;
*//*debug/release 输出:
Whore():0
0
end
~Whore():0
*/
/*下面代码运行成功:错误用法
    Whore wz = fun7();
    cout<<wz.nickname<<endl;
    Whore &&w1 = fun7();
    cout<<w1.nickname<<endl;
    Whore w2 = fun7_1();
    cout<<w2.nickname<<endl;
*//*debug/release输出:
Whore():0
Whore(const Whore &w):1
1
Whore(const Whore &w):2
2
Whore():3
Whore(const Whore &w):4
4
end
~Whore():4
~Whore():2
~Whore():1
~Whore():3
~Whore():0
*/
/*
//下面代码运行成功。
    Whore w;
	fun8(w);
	fun8(Whore());
*//*输出:
Whore():0
void fun8(Whore & w)
Whore():1
void fun8(Whore && w)
~Whore():1
end
~Whore():0
*/

    cout <<"end"<<endl;
    return 0;
}

在C++编程中,了解右值引用并充分利用右值引用,可以减少无意义的拷贝,极大提升程序性能。

int main()
{
    string str = "afef";//调用basic_string(const _Elem *_Ptr)
	string str0 = str; //调用basic_string(const _Myt& _Right)
	cout << "str:" << str << endl << "str0:" << str0 << endl;
	string str1;//调用basic_string()
	str1 = str;//调用_Myt& operator=(const _Myt& _Right)
	cout << "str:" << str << endl << "str1:" << str1 << endl;
	string str2;
	str2 = std::move(str);//调用_Myt& operator=(_Myt&& _Right)  ,remove_reference<_Ty>::type&&move(_Ty&& _Arg)。
//_Myt& operator=(_Myt&& _Right) 中直接夺取了转变为右值引用后的str的内容存储空间,从而减少不必要的拷贝。
	cout << "str:"<<str << endl<<"str2:"<< str2 << endl;
	string str3 = std::move(str0);//调用basic_string(_Myt&& _Right)  ,remove_reference<_Ty>::type&&move(_Ty&& _Arg)
	cout << "str0:" << str0 << endl << "str3:" << str3 << endl;
/*输出:
str:afef
str0:afef
str:afef
str1:afef
str:
str2:afef
str0:
str3:afef
*/

    return 0;
}

统一引用:条款24:区别开统一引用和右值引用_coolmeme的博客-CSDN博客 
引用折叠:主要用于方便编写模板代码,很大程度上减少了因为左右值引用差异而导致的代码的重写
引用折叠和完美转发 - 知乎 
 

引用语义转移std::forward  与 引用语义转换std::move :
 

	// TEMPLATE FUNCTION forward
template<class _Ty> inline
	_CONST_FUN _Ty&& forward(
		typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
	{	// forward an lvalue as either an lvalue or an rvalue
	return (static_cast<_Ty&&>(_Arg));
	}

template<class _Ty> inline
	_CONST_FUN _Ty&& forward(
		typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
	{	// forward an rvalue as an rvalue
	static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
	return (static_cast<_Ty&&>(_Arg));
	}

void tun(int &a)
{
	cout << "lvalue" << endl;
}

void tun(int &&a)
{
	cout << "rvalue" << endl;
}
//声明了引用参数的重载函数就不能再声明无引用参数的重载函数,比如此处声明了tun(int&)或tun(int&&)
//就不能再声明tun(int)。否则会出现
//编辑器可能报错:有多个 重载函数 "tun" 实例与参数列表匹配
//编译器可能报错:“tun”: 对重载函数的调用不明确


//forward的正确用法,起到语义转移的作用,主要如下:(结合引用折叠,下面的模板函数形式中T &&a也被
//称为万能引用,左值引用右值引用参数都能接收)
template<typename T>
void funx(T &&a)  
{
    //tun(a);   //1
    tun(forward<T>(a));  //2 
}
//假设T = “int &&”,引用折叠a为int && 。如果调用1,则a因为已经被初始化会被强制转变成左值引用,
//会走tun(int&)的重载分支输出"lvalue";
//但是如2中那样调用forward,虽然会走forward<int &&>(int &)的重载分支,但最终会返回“int && &&”
//类型,引用折叠后等价于“int &&”,结果输出“rvalue”,相当于将rvalue属性转移到tun中,而不是强制转变
//成左值引用。整个过程相当于将已经初始化的右值引用变量(等价于左值引用)转变成右值引用
//假设T = “int &”,引用折叠后a为int&,如果调用1,会走tun(int &)重载分支输出“lvalue”;
//如果调用2,会走forward<int&>(int&)的重载分支,
//最终会返回“int & &&”,引用折叠后等价于“int &”
//假设T = “int”,引用折叠后a为int &&,调用1,会走tun(int &)的分支输出“lvalue”;如果调用2,
//会走forward<int>(int &)重载分支。最终返回“int &&”类型。

int main()
{
	int &&a1 = 0;
	int &a2 = a1;
	int a3 = a1;
	a3 = a3 + 1;
    
    //通过在forward中设置断点,会发现下面三个都会走forward(type &)的函数。    
    forward<int>(a1); //return type 为 int&&
	forward<int&>(a1);//return type 为 int & &&,引用折叠后等价于int&
    forward<int&>(a2);//return type 为 int & &&,引用折叠后等价于int &
    forward<int&>(a3);//return type 为 int & &,引用折叠后等价于int &
    forward<int&&>(a1);//return type 为 int && &&,引用折叠后等价于int &&
    forward<int&&>(a2);//return type 为int && &&,引用折叠后等价于int &&
    forward<int&&>(a3);//return type 为int &&,引用折叠后等价于int &&
    //通过在forward中设置断点,会发现下面三个都会走forward(type &&)的函数。    
    forward<int>(0);//return type 为int &&,引用折叠后等价于int &&
    forward<int>(move(a1)); //move是个参数为万能引用的函数模板,意义就是将左值引用右值引用转变为右值引用。
    forward<int>(move(a2));
    forward<int>(move(a3));
    //forward<int&>(0); //error: bad forward call
    //forward<int&>(move(a1));//error: bad forward call
    //forward<int&>(move(a2));//error: bad forward call
    //forward<int&>(move(a3));//error: bad forward call
    forward<int&&>(0);//return type 为int && &&,引用折叠后等价于int &&
    forward<int &&>(move(a1));//return type 为int && &&,引用折叠后等价于int &&

	tun(0); //rvalue
	tun(a1); //lvalue a1虽然为右值引用变量,为int && 类型,但是被初始化之后就变成了左值引用
	tun(a2); //lvalue
	tun(a3); //lvalue

    

    tun(forward<int &&>(a1));//rvalue,
    tun(forward<int &&>(a2));//rvalue,
    tun(forward<int &&>(a3));//rvalue,



    return 0;
}

什么是右值,左值,x值,glvalues和prvalues? 学习之路 

cppreference.com关于值类型的详细解读:lvalue,rvalue,xvalue,prvalue,glvalue_杨领well的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值