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(