自C++11以来,引用变得繁多且复杂了,在以往左值引用的基础上新增了右值引用和万能引用,同时提出了引用折叠概念。本文致力于揭开引用的面纱。
左值引用
左值引用即为传统的引用,在C++中用&符号表示。它主要用于绑定左值,视为变量的别名。对于引用的修改,同样会改变被引用值。基于此,函数应用左值引用传参时,函数内部对变量的修改会改变传参时的时参;同时,引用是变量的别名,不会执行对象的拷贝构造过程,可以提高性能。所以,期望函数对形参的修改影响实参时使用使用左值引用作为形参;当不期望函数对形参的修改影响实参时使用const型的左值引用。
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跟随右值引入的概念,右值引用用于绑定右值,通过&&标识,右值引用使用&&标识,但&&标识的不一定是右值引用。
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&&。
template<typename T>
void f(T&& para);//para为万能引用
auto && var2= var1;//var2为万能引用
注意:
可以在函数模板中使用万能引用,但应避免重载具有万能引用型别的函数,因为万能引用型别的函数成为重载候选项时,它会吸引走大片的实参型别,成为重载函数的最佳匹配,则同名的别的函数得不到调用。
区分万能引用和右值引用
右值引用和万能引用均用&&标识,这也导致两者常常混淆。为更好的区分万能引用和右值引用,绘制如下的判断流程图,如下图:
//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++》高博译