我是靠谱客的博主 贪玩水池,最近开发中收集的这篇文章主要介绍C++ reference 那些容易被忽视的细节C++ reference 那些容易被忽视的细节,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
C++ reference 那些容易被忽视的细节
工作之余做个小结。(TE为实体类型,TD为推断类型)
一、 基础知识:
- 引用创建时即初始化赋值。作为函数参数时,程序进入函数就已经初始化完毕。
- 注意区分值与值的引用。
- 一旦创造了
TE&& t
右值引用便有了别名,t的行为变成左值(传入TD&&模板为左引用,),但t打印出的类型依然为右值 - 引用是别名,构造好的引用可当作自身,引用之间的非初始化赋值看const,非const引用像常量一样任意赋值。const引用不能赋值。
- 引用的初始化赋值,比较复杂:对于TE&&:仅能用右值初始化(对于非右值使用forward和move转化)。TE&能用左值,左值引用和右值引用初始化。
- 对于初始化赋值和正常函数参数绑定,规则是相似的:左值引用只能绑定左值,右值引用只能绑定右值,左值常量引用是个例外(可以绑定左值)。
- 对于模板中的通用引用TD&& t, 若直接用TD和t, 则覆盖了foward的合理情况。(通用引用模板相当于生成TE&&和TE&两个模板,由于分别给左值和右值)。
二、std::forward 转化规则(模板中最好仅用于T&&,详解为什么)
std::forward 的作用为:条件转化为右值,一般在含通用引用的模板中使用:
- 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为左值引用
- 在含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运用的地方有:
- 用于disable一些东西,std::reference_wrapper可以将T变为引用(其实现是提取了指针)。为了使用方便cref()和ref()用于创建并返回std::reference_warpper 。但这两个函数对于rvalue是disallow的:
template<class T>
void ref(const T&&) = delete; //同cref
- 这是我思考的一种情形,不知道有没有应用场景。一个类disable了复制构造函数,但enable移动构造函数,此时需要绑定const T&&用于构造const类型
class Example
{
public:
Example();
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 那些容易被忽视的细节所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复