我是靠谱客的博主 贪玩水池,最近开发中收集的这篇文章主要介绍C++ reference 那些容易被忽视的细节C++ reference 那些容易被忽视的细节,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

最后

以上就是贪玩水池为你收集整理的C++ reference 那些容易被忽视的细节C++ reference 那些容易被忽视的细节的全部内容,希望文章能够帮你解决C++ reference 那些容易被忽视的细节C++ reference 那些容易被忽视的细节所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部