C++ reference 那些容易被忽视的细节

本文深入探讨C++中引用的各种细节,包括基础知识、std::forward的转化规则、const右值引用的作用、如何区分通用引用和右值引用以及引用间的赋值关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++ reference 那些容易被忽视的细节

工作之余做个小结。(TE为实体类型,TD为推断类型)

一、 基础知识:

  1. 引用创建时即初始化赋值。作为函数参数时,程序进入函数就已经初始化完毕。
  2. 注意区分值与值的引用。
  3. 一旦创造了TE&& t右值引用便有了别名,t的行为变成左值(传入TD&&模板为左引用,),但t打印出的类型依然为右值
  4. 引用是别名,构造好的引用可当作自身,引用之间的非初始化赋值看const,非const引用像常量一样任意赋值。const引用不能赋值。
  5. 引用的初始化赋值,比较复杂:对于TE&&:仅能用右值初始化(对于非右值使用forward和move转化)。TE&能用左值,左值引用和右值引用初始化。
  6. 对于初始化赋值和正常函数参数绑定,规则是相似的:左值引用只能绑定左值右值引用只能绑定右值左值常量引用是个例外(可以绑定左值)
  7. 对于模板中的通用引用TD&& t, 若直接用TD和t, 则覆盖了foward的合理情况。(通用引用模板相当于生成TE&&和TE&两个模板,由于分别给左值和右值)。

二、std::forward 转化规则(模板中最好仅用于T&&,详解为什么)

std::forward 的作用为:条件转化为右值,一般在含通用引用的模板中使用:

  1. std::forward(t)的转化依赖Type类型:
// forward的其中一个实现
template<typename Tp>
forward(typename remove_reference<Yp>::type& _t) no except{
	return static_cast<Tp&&>(t);}
//其完整的行为如下(不考虑const)
std::forward<TE>(...)     => TE&&   (Tp = TE)总转化为右值,结合其定义会令人困惑
std::forward<TE&>(TE&)    => TE&    (Tp = TE&,&  && == & )
std::forward<TE&&>(...)   => TE&&   (Tp = TE&&,&& && == &&)
//其余非法forward<TE&>(TE&&),不能将右值forward为左值引用 
  1. 在含TD&&模板中,很好的适配Type-Deduction:
template<typename T>
void foo(T&& t){        
	std::forward<T>(t); 
}
T的类型为TE或TE&,对应t类型为TE&&或TE&,forward对应forward类型TE&&,TE&,见附录。
TE&& -> TE&&
const TE& -> const TE&
TE&/TE -> TE&

三、 关于const TE &&的作用

const TE&& 不是特别make sense,在对const 使用std::move和移动构造函数很违反逻辑,为什么能移动却叫const呢?而且const-rvalue-ref能做的const-lvalue-ref也能做。搜尽资料,const-rvalue-ref运用的地方有:

  1. 用于disable一些东西,std::reference_wrapper可以将T变为引用(其实现是提取了指针)。为了使用方便cref()和ref()用于创建并返回std::reference_warpper 。但这两个函数对于rvalue是disallow的:
template<class T>
void ref(const T&&) = delete; //同cref
  1. 这是我思考的一种情形,不知道有没有应用场景。一个类disable了复制构造函数,但enable移动构造函数,此时需要绑定const T&&用于构造const类型
class Example
{
publicExample();
    Example(const Example& e) = delete;
    Example(const Example&& e){}; 
};
const Example e;
const Example moveE(std::move(e));   //此时匹配const T&&

四、 如何区分universial reference 还是 rvalue reference

对于需要Type-Deduction的地方为universial reference。
对于实体类型,为rvalue reference。

template<typename T>
void func(std::remove_reference<T>::type&& t){} // is rvalue ref, not universal ref

五、 reference之间的赋值关系(细节)

注:有名字的右数值引用用TE&&表示,右值代指无名的变量。

// 以下初始化赋值成立(同理函数参数绑定也一样规则):
TE&        <- TE,TE&,TE&&
const TE&  <- TE,TE&,TE&&,const TE,const TE&,const TE&&,右值
TE&&       <- std::forward(TE&&),右值
const TE&& <- std::forward(TE&&),std::forward(const TE&&),右值
// 以下非初始化赋值成立
TE&        <- TE,const TE,TE&,const TE&,TE&&,const TE&&,右数值
TE&        <- TE,const TE,TE&,const TE&,TE&&,const TE&&,右数值

六、 附录Boost打印结果:

一般情况

int&& RR = 1;            RR  => int && 但行为像int&
int&  R  = RR;           R   => int&
std::forward<int>(...)   ret => int&&
std::forward<int&>(TE&)  ret => int&
std::forward<int&&>(...) ret => int&&
//其余(T=TE&,t=TE&&)非法

TD&&的模板

template<typname T>
void func(T&& t) {}
int   V  = 1;
int&  R  = V;
int&& RR = 1;
//类型:                       T         t          forward<T>(t)
func(1) :                     int       int&&          int&&
func(V) :                     int&      int&           int&
func(R) :                     int&      int&           int&
func(RR):                     int&      int&           int&
func(std::forward<int>(RR)):  int       int&&          int&&

TE&&的函数

void func(int&& t){};
//仅能通过如下调用:
func(1);
func(std::move(A));
func(std::forward<int>(A));  

TD&的模板(注意T和forward,在此模板不要随便用forward)

template<typname T>
void func(T& t) {}
int   V  = 1;
int&  R  = V;
int&& RR = 1;
//类型:                       T         t          forward<T>(t)!!!(ATTENTION)
func(1) :                     no
func(V) :                     int      int&           int&&
func(R) :                     int      int&           int&&
func(RR):                     int      int&           int&&
func(std::forward<int>(RR)):  no
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值