catalog
is_lvalue_reference
using T = QString;
using TT = T &;
using TTT = T &&;
using A = QString *;
using AA = A &;
using AAA = A &&;
DE<< ::std::is_lvalue_reference< T>::value << ", " << ::std::is_rvalue_reference< T>::value;
DE<< ::std::is_lvalue_reference< TT>::value << ", " << ::std::is_rvalue_reference< TT>::value;
DE<< ::std::is_lvalue_reference< TTT>::value << ", " << ::std::is_rvalue_reference< TTT>::value;
DE<< ::std::is_lvalue_reference< A>::value << ", " << ::std::is_rvalue_reference< A>::value;
DE<< ::std::is_lvalue_reference< AA>::value << ", " << ::std::is_rvalue_reference< AA>::value;
DE<< ::std::is_lvalue_reference< AAA>::value << ", " << ::std::is_rvalue_reference< AAA>::value;
false , false
true , false
false , true
false , false
true , false
false , true
右值引用作为函数参数
一个临时对象Foo(), 会优先进入 [const] Foo &&, 而其次才是 const Foo &
如果, 既有Foo 又有[const] Foo &&, 两个重载; 此时, Foo()临时对象 同时满足两个 ==此时会报错的!!! ambiguous ==
即重载函数, [const] T && 和 T, 只能选择1个
最标准的写法是:
一个是左值引用: const T &, 一个是右值引用T &&
我们知道, 一般一个类, 会有: (左值拷贝构造) 和 (右值拷贝构造), 他的最标准写法是:
class Foo{
Foo( const Foo &){
}
Foo( Foo &&){
}
}
下面解释下, 为什么是&&, 而不是const &&
func( T && _t)
此时的这个_t, 一定是一个临时对象; 什么叫"临时对象"呢??? 外界一定不会(访问/修改)这个临时对象!!
因为, 这个临时对象, 他是匿名的; 外界根本获取不到这个临时对象;
既然外界不会用到这个对象, 那么, 不写const; 此时, 在这个函数里 就可以直接使用(修改)他
临时对象 与 右值
&Foo();
&(Foo().data);
Foo().data = 123;
以上这3个操作, 都是非法的; 因为, 对一个右值 进行 左值操作, 是不允许的!!!
此时引入(右值引用), 他有2个功能:
- 右值引用, 可以延长 右值(临时对象)的生命期;
本来临时对象现在马上就要摧毁, 现在有个 (右值引用) 引用上他, 这个临时对象的生命期, 就和这个 (右值引用) 一样的 - 右值引用, 本身是(左值); 即, 右值引用 可以进行 (左值操作)
即, 右值引用给了我们一个渠道, 我们可以 使用 左值操作 来操作一个 临时对象!!
全程, 只会产生1个对象void func( Foo && f){ ' 这个f, 就是那个Foo()临时对象, 两个是同一个对象 ' &f; ' 获取 临时对象的地址 ' &(f.data); ' 成员变量地址 ' f.data = 123; ' 成员变量赋值 ' } int main(){ func( Foo()); }
引用/指针的隐患
引用传参
class Foo{
const T * ref;
void func( [const] T & _o){
ref = &_o;
}
}
虽然这个ref成员变量, 是const的; 但, 这依然是危险的!!!
因为外界在调用func时: func( obj), 这个T obj的生命周期 可能会早于 Foo对象的生命!!!
导致, T obj已经销毁了, 而ref里还存着他的地址!!! 非常危险
你必须要自己保证: ref的生命期 是早于 他所引用的对象!!!
返回值为引用
class Foo{
T data;
[const] T & get(){
return data;}
};
当你[const] T * ref; ref = &( foo.get());时, 问题和上面是类似的
你的foo生命期, 可能是 早于 ref的; 此时: ref里 存了一个 已经销毁的对象; 这当然是危险的
一个处理办法是: 不要使用(指针)
如果你使用: [const] T & ref = foo.get();, 由于引用必须初始化; 所以, ref的生命期 一定早于 foo
但是, 你无法控制(用户)…
总之, 你返回一个[const]引用, 就是有隐患的, c++并不推荐这样错!!!
偶尔, 可以使用; 但你必须要, 自己确保 安全性!!!
比如, stl的operator[]就是返回的 [const]引用; 但这并不是常态;
补充
即使你使用引用, 还是有隐患!!!
class ST{
public:
string str;
string & get(){
return str;}
};
当你使用: string & ref = ST().get();时, 此时, 引用再一次指向了: 已经delete了的内存!!!
这是隐蔽的 危险的 bug…
当你使用: T & ref = AA.BB.CC.get()时, 不能只看get() 返回的是T &, 就以为 万事如意了
你还必须要保证: AA, BB, CC都是引用, 绝不会产生临时对象!!!
引用是右值
void func( int && _d0){
}
{
int a = 123;
func( ?);
}
func( 123) 是可以的 123是常量, 右值
func( a) 是错误的!!! a是左值; 右值绑定_d0 不可以绑定到 左值上
但是
using Type = int &;
void func( Type && _d0){
}
{
int a = 123;
func(
本文详细探讨了C++中的左值引用和右值引用,包括它们的作用和使用场景。重点讲解了右值引用如何延长临时对象的生命周期,以及如何用于移动构造和移动赋值,提高效率。同时,强调了引用作为函数参数和返回值时的安全隐患,并通过`std::move`展示了转换左值引用和右值引用的方法。
最低0.47元/天 解锁文章
986

被折叠的 条评论
为什么被折叠?



