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; }
Demo x=foo();
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函数定义在标准库
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
所以最终版本
template<typename T> void cover(T&& para){ func(forward(forward<T>(para))); }
std::forward的实现与static_cast
std::forward函数的用法为forward
总结
以上就是关于C++11中右值引用、转移语义和完美转发的全部内容,这篇文章介绍的很详细,希望对大家的学习工作能有所帮助。
更多浅析C++11中的右值引用、转移语义和完美转发相关文章请关注PHP中文网!