C++11의 rvalue 참조, 전송 의미 체계 및 완벽한 전달에 대한 간략한 분석

高洛峰
풀어 주다: 2017-01-23 14:11:24
원래의
1462명이 탐색했습니다.

1. Lvalue와 rvalue:

C++에는 lvalue와 rvalue에 대한 표준 정의가 없지만 널리 알려진 속담이 있습니다. 임시가 아닌 것은 lvalue이고, 주소를 가질 수 없고, 이름이 없고, 임시적인 것은 rvalue입니다.

즉치수, 함수에서 반환하는 값 등은 모두 rvalue임을 알 수 있습니다. 익명 개체(변수 포함) 대신 함수 참조, const 개체 등이 반환하는 값은 모두 lvalue입니다.

기본적으로 이해하면 프로그래머는 배후에서 컴파일러에 의해 제어됩니다. 이 코드 줄에서 유효한 것이 rvalue(즉각적 데이터 포함)인지 확인하고, 범위 지정 규칙을 통해 수명을 알 수 있는 사용자가 만든 것은 lvalue(함수 및 const에 의해 반환된 지역 변수에 대한 참조 포함)인지 확인하세요. 객체), 예:

int& foo(){int tmp; return tmp;}
 
int fooo(){int tmp; return tmp;}
 
int a=10;
 
const int b;
 
int& temp=foo();//虽然合法,但temp引用了一个已经不存在的对象
 
int tempp=fooo();
로그인 후 복사

위 코드에서 a, temp 및 foo()는 모두 상수 lvalue가 아니며, b는 상수 lvalue입니다. fooo()는 상수가 아닌 rvalue이고 10은 상수 rvalue입니다. 특별히 주의해야 할 사항이 있습니다. 반환된 참조는 lvalue입니다(주소를 사용할 수 있음).

일반적으로 컴파일러는 rvalue에 대한 변경을 허용하지 않습니다(rvalue의 수명은 프로그래머에 의해 제어되지 않기 때문에 rvalue가 변경되더라도 사용하지 못할 수 있음), 특히 빌드된 경우 -in 유형 객체. 그러나 C++에서는 rvalue 객체를 사용하여 멤버 함수를 호출할 수 있지만 같은 이유로 그렇게 하지 않는 것이 가장 좋습니다.

2.

Rvalue 참조 rvalue에 바인딩되면 rvalue에 바인딩된 후 소멸될 rvalue의 수명이 바인딩된 rvalue 참조의 수명까지 연장됩니다. 참조는 lvalue 참조를 대체하는 것이 아닙니다. 대신 rvalue(특히 임시 개체) 구성을 최대한 활용하여 개체 생성 및 삭제 작업을 줄여 효율성을 향상하세요.

Datatype&& variable
로그인 후 복사

컴파일러가 RVO(반환 값 최적화) 최적화를 수행하지 않는다는 전제 하에 다음 작업이 수행됩니다.

(Demo是一个类)
Demo foo(){
  Demo tmp;
  return tmp;
}
로그인 후 복사

생성자(tmp의, x, 임시 객체), 그에 따라 소멸자는 객체가 파괴될 때 세 번 호출되며, rvalue 참조가 사용되는 경우:

Demo x=foo();
로그인 후 복사

그러면 x를 구성할 필요가 없고, 원래 소멸하려고 했던 임시 객체도 x의 바인딩으로 인해 x와 동일하게 수명이 연장됩니다(이렇게 이해하면 됩니다). x는 임시 개체에 법적 상태(이름)를 부여하므로 효율성을 개선해야 합니다(tmp가 4바이트의 공간을 차지해야 한다는 비용이 들지만 이는 중요하지 않습니다).

rvalue 참조에 대한 바인딩 규칙입니다. 및 lvalue 참조:

상수 왼쪽 값 참조는 상수 및 상수가 아닌 lvalue, 상수 및 상수가 아닌 rvalue에 바인딩될 수 있습니다.

상수가 아닌 lvalue 참조는 비상수 lvalue에만 바인딩될 수 있습니다. 상수 lvalue; 값 참조는 const가 아닌 rvalue에만 바인딩될 수 있습니다(vs2013은 상수 rvalue에도 바인딩될 수 있음). 이는 의미의 완전성을 위해서만 존재하며 상수 lvalue 참조는 해당 역할을 수행할 수 있습니다. 🎜> 상수 lvalue 참조도 rvalue에 바인딩될 수 있다는 것이 바인딩 규칙에서 볼 수 있지만 분명히 그렇지 않습니다. rvalue 참조는 일반적으로 변경되기 때문에 rvalue 참조를 사용하여 rvalue 값을 변경할 수 있습니다. 바인딩된 rvalue, 바인딩된 rvalue는 const일 수 없습니다.

참고:Rvalue 참조는 lvalue입니다!

3. 이동 의미:

rvalue 참조의 목적 중 하나가 도입되었습니다. 전송 의미론을 구현하는 것은 리소스(힙, 시스템 개체 등)를 한 개체(일반적으로 익명의 임시 개체)에서 다른 개체로 이동할 수 있으므로 개체 생성 및 삭제 작업이 줄어들고 프로그램 효율성이 향상됩니다(이것은 이미 설명되어 있음). 2)의 예에서는 복사 의미론(Copy Semantics)과 반대되는 개념으로, 사실 전달 의미론은 C++98의 언어와 라이브러리에서 이미 사용된 개념이다. /03, 다음은 복사 생성자의 생략(일부 상황에서는 복사 생성자 제거), 스마트 포인터 복사(auto_ptr "copy"), 연결된 목록 접합(list::splice) 및 컨테이너에서의 스왑입니다. (컨테이너에서 스왑) 등이 있지만 아직 통합되지 않았습니다.

일반 함수와 연산자는 rvalue 참조를 사용하여 전송 의미(예: 2의 예)를 구현할 수도 있습니다. 의미론은 일반적으로 전송 생성자와 전송 할당 연산자를 통해 구현됩니다. 전송 생성자의 프로토타입은 Classname(Typename&&)인 반면, 복사 생성자의 프로토타입은 Classname(const Typename&)입니다. 전송 생성자는 컴파일러에 의해 자동으로 생성되지 않으며 필요합니다. 전송 생성자를 정의하는 것만으로는 컴파일에 영향을 주지 않습니다. 전달된 매개변수가 lvalue이면 복사 생성자가 호출됩니다. > 예:

Demo&& x=foo();
로그인 후 복사

从以上代码可以看出,拷贝构造函数在堆中重新开辟了一个大小为10000的int型数组,然后每个元素分别拷贝,而转移构造函数则是直接接管参数的指针所指向的资源,效率搞下立判!需要注意的是转移构造函数实参必须是右值,一般是临时对象,如函数的返回值等,对于此类临时对象一般在当行代码之后就被销毁,而采用转移构造函数可以延长其生命期,可谓是物尽其用,同时有避免了重新开辟数组.对于上述代码中的转移构造函数,有必要详细分析一下:

Demo(Demo&& lre):arr(lre.arr),size(lre.size)({lre.arr=NULL;}
로그인 후 복사

lre是一个右值引用,通过它间接访问实参(临时对象)的资源来完成资源转移,lre绑定的对象(必须)是右值,但lre本身是左值;

因为lre是函数的局部对象,”lre.arr=NULL"必不可少,否则函数结尾调用析构函数销毁lre时仍然会将资源释放,转移的资源还是被系统收回.

4. move()函数

3中的例子并非万能,Demo(Demo&& lre)的实参必须是右值,有时候一个左值即将到达生存期,但是仍然想要使用转移语义接管它的资源,这时就需要move函数.

std::move函数定义在标准库中,它的作用是将左值强行转化为右值使用,从实现上讲,std:move等同于static_cast(lvalue) ,由此看出,被转化的左值本身的生存期和左值属性并没有被改变,这类似于const_cast函数.因此被move的实参应该是即将到达生存期的左值,否则的话可能起到反面效果.

5. 完美转发(perfect forwarding)

完美转发指的是将一组实参"完美"地传递给形参,完美指的是参数的const属性与左右值属性不变,例如在进行函数包装的时候,func函数存在下列重载:

void func(const int);
void func(int);
void func(int&&);
로그인 후 복사

如果要将它们包装到一个函数cover内,以实现:

void cover(typename para){
  func(para);
}
로그인 후 복사

使得针对不同实参能在cover内调用相应类型的函数,似乎只能通过对cover进行函数重载,这使代码变得冗繁,另一种方法就是使用函数模板,但在C++ 11之前,实现该功能的函数模板只能采用值传递,如下:

template<typename T>
void cover(T para){
  ...
  func(para);
  ...
}
로그인 후 복사

但如果传递的是一个相当大的对象,又会造成效率问题,要通过引用传递实现形参与实参的完美匹配(包裹const属性与左右值属性的完美匹配),就要使用C++ 11 新引入的引用折叠规则:

函数形参 T的类型 推导后的函数形参

T& A& A&
T& A&& A&
T&& A& A&
T&& A&& A&&

因此,对于前例的函数包装要求,采用以下模板就可以解决:

template<typename T>
void cover(T&& para){
  ...
  func(static_cast<T &&>(para));
  ...
}
로그인 후 복사

如果传入的是左值引用,转发函数将被实例化为:

void func(T& && para){
 
  func(static_cast<T& &&>(para));
 
}
로그인 후 복사

应用引用折叠,就为:

void func(T& para){
 
  func(static_cast<T&>(para));
 
}
로그인 후 복사

如果传入的是右值引用,转发函数将被实例化为:

void func(T&& &¶){
 
   func(static_cast<T&& &&>(para));
}
로그인 후 복사

应用引用折叠,就是:

void func(T&& para){
 
  func(static_cast<T&&>(para));
 
}
로그인 후 복사

对于以上的static_cast ,实际上只在para被推导为右值引用的时候才发挥作用,由于para是左值(右值引用是左值),因此需要将它转为右值后再传入func内,C++ 11在定义了一个std::forward函数来实现以上行为,

所以最终版本

template<typename T>
 
void cover(T&& para){
 
  func(forward(forward<T>(para)));
 
}
로그인 후 복사

std::forward的实现与static_cast(para)稍有不同

std::forward函数的用法为forward(para) , 若T为左值引用,para将被转换为T类型的左值,否则para将被转换为T类型右值

总结

以上就是关于C++11中右值引用、转移语义和完美转发的全部内容,这篇文章介绍的很详细,希望对大家的学习工作能有所帮助。

更多浅析C++11中的右值引用、转移语义和完美转发相关文章请关注PHP中文网!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!