前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++引用,这篇文章讲全了

C++引用,这篇文章讲全了

作者头像
程序员的园
发布2024-07-18 13:31:36
180
发布2024-07-18 13:31:36
举报
文章被收录于专栏:程序员的园——原创文章

自C++11以来,引用变得繁多且复杂了,在以往左值引用的基础上新增了右值引用和万能引用,同时提出了引用折叠概念。本文致力于揭开引用的面纱。

左值引用

左值引用即为传统的引用,在C++中用&符号表示。它主要用于绑定左值,视为变量的别名。对于引用的修改,同样会改变被引用值。基于此,函数应用左值引用传参时,函数内部对变量的修改会改变传参时的时参;同时,引用是变量的别名,不会执行对象的拷贝构造过程,可以提高性能。所以,期望函数对形参的修改影响实参时使用使用左值引用作为形参;当不期望函数对形参的修改影响实参时使用const型的左值引用。

代码语言:javascript
复制
void using_lvalue_ref()
{
    int x = 10;         // x 是左值
    int& ref = x;       // ref 是 x 的左值引用
    ref = 20;           // 修改 x 的值为 20
    std::cout << x;     // 输出:20
}


void swap(int& a, int& b)
{
    int tmp = a;
    a=b;
    b=tmp;
}


void using_swap()
{
    int a =5,b=20;
    std::cout << "before swap a=" << a << " and b=" << b << std::endl;
    swap(a,b);
    std::cout << "after swap a=" << a << " and b=" << b << std::endl;
}



struct BigData{};
void Process(const BigData& data)
{
    //-----
}

右值引用

右值引用是C++11跟随右值引入的概念,右值引用用于绑定右值,通过&&标识,右值引用使用&&标识,但&&标识的不一定是右值引用。

代码语言:javascript
复制
void using_right_value_ref()
{
    int&& rref = 5;     // rref 是 5 的右值引用
    People && p = People("danney",30);
    People tom("tom",66);
    People&& rr_tom = std::move(tom);//right_value
}

万能引用

万能引用也是C++11引入的概念,万能引用因其可以绑定的对象种类繁多而得名,其可以绑定左值也可以绑定右值,其既可以绑定const对象也可以绑定非const对象,其既可以绑定volatile对象也可以绑定到非volatile对象,甚至绑定到同时含有const和volatile的对象。

万能引用使用&&标识,通常涉及到型别推导,形如auto&&或模板形参T&&。

代码语言:javascript
复制
template<typename T>
void f(T&& para);//para为万能引用


auto && var2= var1;//var2为万能引用

注意:

可以在函数模板中使用万能引用,但应避免重载具有万能引用型别的函数,因为万能引用型别的函数成为重载候选项时,它会吸引走大片的实参型别,成为重载函数的最佳匹配,则同名的别的函数得不到调用。

区分万能引用和右值引用

右值引用和万能引用均用&&标识,这也导致两者常常混淆。为更好的区分万能引用和右值引用,绘制如下的判断流程图,如下图:

  • 看到&&首先判断是否涉及到型别推导,若不涉及型别推导,则为右值引用;
  • 如果涉及型别推导且为auto&&形式,为万能引用,参见example1
  • 若涉及型别推导,则判断&&前是否为T&&(此处T非特指T,可以是任意的类型名,如typename C,此处的T便是C),如果不是,则为右值引用,参见example2;
  • 如果是T&&,则观察是否存在const/volatile修饰,若有,则其为右值引用;参见example3;
  • 如果没有修饰词,则判断该T类别推导是该函数的吗,如果不是该函数,则为右值引用,参见example4;
  • 如果T类别推导是该函数,则为万能引用,
代码语言:javascript
复制
//example1
auto && var2= rr_tom;//var2为万能引用


//example 2
//非M&&形式,为右值引用,
template<typename M>
void f2(std::vector<M>&& para);



//example 3
//含修饰词,为右值引用
template<typename T>
void f31(const T&& para);


template<typename T>
void f32(volatile T&& para);


//example 4
template <class_Ty>
class My_vector { // varying size array of values


public:
    //_Ty取决于类模板;,_val右值引用
    void push_back(_Ty&&_Val);


    //_Valty取决于函数模板,_val万能引用
    template <class... _Valty>
    _CONSTEXPR20 decltype(auto) emplace_back(_Valty&&... _Val) ;
};

引用折叠

当初始化的形参为万能引用时,如果实参是左值,则推导为左值引用,如果实参是右值,则推导为右值引用。

这是由于在C++中,引用的引用是非法的,所以当引用与引用作用到一起时会发生引用折叠。原始引用中任一引用为左值引用,则为左值引用,否则为右值引用。

引用折叠发生的四种语境:auto推导,模板实例化,创建或运用typedef和using别名声明以及decltype。综合如上的四种场景,其实都是在型别推导过程中发生引用折叠。

总结

本文讲解了左值引用、右值引用及万能引用,并着重区分了右值引用和万能引用,不涉及型别推导的都是右值引用,但是涉及型别推导的不一定是万能引用。引入了引用折叠概念,

参考

《Effective Modern C++》高博译

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档