c++左值引用和右值引用,c++ 左值引用,详解C++右值引用

c++左值引用和右值引用,c++ 左值引用,详解C++右值引用

很多初学者觉得正确的值引用晦涩难懂,其实不然。右值引用只是一种新的C语法。真正难以理解的是基于右值引用的两个C编程技巧,即移动语义和完美转发。本节向读者解释什么是正确的值引用及其基本用法。

目录

移动语义完美转发概述

概述

在C中,常量、变量或表达式必须是左值(lvalue)或右值(rvalue)。

左值:非临时(已命名,可在多个语句中使用,可寻址)。可以出现在等号的左边或右边。可分为非常左值和常左值。

右值:temporary(匿名,只在当前语句中有效,不能带地址)。只能出现在等号的右边。可分为非常右值和常右值。

左值引用:对左值的引用是左值引用。可分为非恒定左值参考和恒定左值参考。

注意:常量左值引用是一种“通用”的引用类型,可以绑定所有类型的值,包括非常量左值、常量左值、非常量右值和常量右值。

右值引用:对右值的引用就是右值引用。可分为非恒定右值参考和恒定右值参考。

作为临时对象的右值,它的生命周期很短,一般是在当前表达式执行完之后才释放。

将它赋给正确的值引用,就可以“更新”它,而不需要昂贵的复制操作,它的生命周期和正确的值引用类型变量一样长。

右值指称的两个基本特征:移动语义和完美转发。

移动语义(Move Semantics)

您可以将资源从一个对象转移到另一个对象;主要解决减少不必要的临时对象的创建、复制和销毁的问题。

移动构造函数MyClass(类型a):当构造函数参数为右值时,移动构造函数优先于复制构造函数MyClass(常量类型a)。

移动赋值运算符类型operator=(类型a):当赋值为右值时,移动赋值运算符类型operator=(常量类型a)是首选。

#包括iostream

#包含字符串

#包含实用程序

结构MyClass

{

STD:string s;

MyClass(const char* sz) : s(sz)

{

STD:cout ' my class SZ:' SZ STD:endl;

}

MyClass(const MyClass o) : s(o.s)

{

std:cout '复制构造!\ n ';

}

my class(my class o)no exception:s(STD:move(o . s))

{

std:cout 'move构造!\ n ';

}

my class operator=(const my class other){//复制赋值

std:cout '复制赋值!\ n ';

s=other.s

返回* this

}

MyClass运算符=(my class other)no except {//move assign

std:cout '移动赋值!\ n ';

s=STD:move(other . s);

返回* this

}

静态MyClass GetMyClassGo(const char * SZ)

{

my class o(SZ);//注意:可能会被NRVO优化。

返回o;

}

};

void func0(MyClass o)

{

STD:cout o . s . c _ str()STD:endl;

}

void func1(MyClass o)

{

STD:cout o . s . c _ str()STD:endl;

}

void func2(const MyClass o)

{

STD:cout o . s . c _ str()STD:endl;

}

void func3(MyClass o)

{

STD:cout o . s . c _ str()STD:endl;

}

int main(int arg,char* argv[])

{

my class a1(' how ');

my class a2(' are ');

a2=a1//copy assign注意:a1是左值。

a2=my class(' you ');//move assign注意:MyClass('you ')是右值。

my class a3(a1);//复制构造注意:a1是左值。

my class a4=my class:GetMyClassGo(' go ');//move构造注意:它发生在MyClass:GetMyClassGo()内部

my class a5=my class:GetMyClassGo(' China ');//移动构造注释两次:一次发生在MyClass:GetMyClassGo()内部;另一次发生在返回值赋给a5的时候。

my class a6(' let ');

my class a7(' it ');

my class A8(' go ');

MyClass a9('!');

func 0(a6);//复制构造

func 1(a7);

func 2(A8);

//func 3(a9);//编译错误:不能将左值赋给右值。

func 0(my class:GetMyClassGo(' god '));//移动构造注释两次:一次发生在MyClass:GetMyClassGo()内部;另一种情况是将返回值赋给foo0参数。

//func 1(my class:GetMyClassGo(' is '));//编译错误:不能将右值赋给左值。

func 2(my class:GetMyClassGo(' girl '));//move构造注意:它发生在MyClass:GetMyClassGo()内部

func3(MyClass:GetMyClassGo('!'));//move构造注意:它发生在MyClass:GetMyClassGo()内部

返回0;

}

注意:要测试上述代码,您必须关闭C编译器优化技术- RVO,NRVO和复制省略。

使用std:move实现移动语义。

将左值或右值强制转换为右值引用。注意:UE4对应的是MoveTemp模板函数。

Std:move(en chs)不移动任何东西,它只是将一个对象的状态或所有权从一个对象转移到另一个对象。注意:只是转移,没有内存重定位或者内存复制。

基本类型后(如int、double等。)被std:move移动,其值不会改变。

复合类型被std:move移动后,处于未定义但有效的状态(大部分成员函数还是有意义的)。例如,标准库中的容器类对象被移动后,它将成为一个空容器。

完美转发(Perfect Forwarding)

对于模板函数,使用通用引用将一组参数原封不动地传递给另一个函数。

不变的意思是:左值,右值,是否为const不变。带来以下三个好处:

确保左值和右值的属性。

避免不必要的复印操作。

避免模板函数需要左值、右值或const参数来实现不同的重载。

通用引用(转发引用)是一种特殊类型的模板引用,它采用右值引用的语法形式(但不是右值引用)。例如模板类T void func(T t) {}

t当自动类型推断发生时,它是一个未确定的通用引用类型,t取决于传递的参数t是右还是左。右值通过T变成右值引用,左值通过T变成左值引用。

Std:move是用万能引用实现的。其定义如下:

模板类型名T

typename remove _ reference:type move(T T)

{

返回static _ cast typename remove _ reference:type(t);

}

/*****************************************

函数的作用是从类型中移除引用。

STD:remove _ reference et:type-T

STD:remove _ reference et:type-T

STD:remove _ reference et:type-T

******************************************/

//最原始、最常见的版本

模板typename T struct remove _ reference {

typedef T类型;//将T的类型别名定义为type

};

//有些版本比较特殊,会用于左值引用和右值引用。

template t struct remove _ reference t//左值引用

{ typedef T类型;}

template tstruct remove _ reference//右值引用

{ typedef T类型;}

当t为左值时,展开为:U move(U t)注意:右值指的是类型变量,也是左值。

当t为右值时,展开为:U move(U t)

最后,通过static_cast强制类型转换返回正确的值引用。注意:static_cast之所以可以使用类型转换,是为了通过remove _ reference:type:type模板移除t和t的引用,从而获得特定的类型t(模板特化)。

报价折叠

规则:具有左值的引用是具有左值的引用,否则是具有右值的引用。

使用std:forward实现参数的完美转发。其定义如下(en chs):

模板类型名T

T forward(remove_reference_tT参数)//将左值作为左值或右值转发

{

返回static _ castT(arg);

}

模板类型名T

T forward(remove_reference_tT参数)//将右值作为右值转发

{

static_assert(!is_lvalue_reference_vT,'错误的前向调用');

返回static _ castT(arg);

}

最后通过static_cast对引用进行折叠,经过强制类型转换,实现了原封不动的转发参数。注:UE4对应正向模板功能

空栏(int a,int b)

{

int c=a b;

}

void func(int a,int b)

{

int c=a b;

}

模板类型名A,类型名B

Void foo(A a,B b) {//a,B是左值引用或右值引用。

bar(std:forwardA(a),STD:forward b(b));//STD:forward转发前后,参数A和B的类型完全不变。

}

int main(int arg,char* argv[])

{

int a=10

foo(a,20);//扩展到void foo(int a,int b)。std:forward完美转发后,会调用void bar(int a,int b)函数。

func(std:forwardint(a),STD:forward int(30));//STD:forward完美转发后,会调用void func(int a,int b)函数。

返回0;

}

以上是C右值参考的详细内容。更多关于C右值参考的信息,请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • vs2015打包安装程序,vs2015程序打包,VS2022实现VC++打包生成安装文件图文详细历程
  • vc++6.0的快捷键,vc 快捷键
  • vc++6.0的快捷键,vc 快捷键,VC6.0常用快捷键大全
  • 绘制圆角矩形的方法,c++ 画矩形,C#画圆角矩形的方法
  • 懒汉式和饿汉式代码,单列模式懒汉和饿汉,C++单例模式的懒汉模式和饿汉模式详解
  • 好用的C++编译器,c++编译软件哪个好
  • semaphore c#,c++ semaphore
  • semaphore c#,c++ semaphore,C++中Semaphore内核对象用法实例
  • dev-c++使用教程,dev c++安装教程
  • dev-c++使用教程,dev c++安装教程,Dev C++ 安装及使用方法(图文教程)
  • C里面指针常量和常量指针的区别,c++指针常量和常量指针
  • C里面指针常量和常量指针的区别,c++指针常量和常量指针,简单总结C++中指针常量与常量指针的区别
  • com组件初始化失败,c#开发com组件,C++中COM组件初始化方法实例分析
  • c++静态成员变量使用,c++静态成员函数和静态成员变量
  • c++静态成员变量使用,c++静态成员函数和静态成员变量,详解c++ 静态成员变量
  • 留言与评论(共有 条评论)
       
    验证码: