引用折叠 & 万能引用 & 模板参数推导

引用折叠 & 万能引用 & 模板参数推导

三个适合放一起理解,把之前记录的笔记整合分享下~

引用折叠规则:

& + & -> &
& + && -> &
&& + & -> &

&& + && -> &&

只有右值引用 + 右值引用才能折叠为右值引用,其余均折叠为左值引用

万能引用

一般在在模板中,且既能接受左值引用参数又能接受右值引用参数

template
ReturnType Function(T&& parem)
{
    // 函数功能实现
}

为什么得是T&&不是T or T&?
T:万能引用首先得是引用,T的话是值传递了

template
void testfunc(T ¶m);

int a = 0;
testfunc(a);       	 // T为int,param为int
testfunc(a); 		 // T为int&,param为int&
testfunc(1); 	// T为int&&,param为int&&

T&:能接收任何引用但不能推导为右值引用

template
void testfunc(T ¶m);

int a = 0;
testfunc(a);        	// T为int,param为int&
testfunc(a);  		// T为int&,param为int& & = int&
testfunc(1); 	// ERR,T为int&&,param为int&& & = int&,所以不能接受右值所以报错

这样无论T推导为什么类型,传递进来的param一定是个引用(fix上面的问题)
但可以看到testfunc(1)出现错误,原因就是引用折叠下能够折叠为右值只存在一种情况,即int&& &&
所以T&永远接受不了右值传入
T&&:万能引用

template
void testfunc(T&& param)
{
    // ...
}
int a = 0;
testfunc(a);  // T推导为int&,param为int& && = int&
testfunc(1); // T推导为int&&,param为int&& && = int&&

模板参数推导规则

上面都是手动指定的模板参数,和自动推导是不一样的,看下自动推导的规则吧

template<typename T>
void f(ParamType param);

f(expr);    // call f with some expression

自动推导过程分两部分,一是T的推导,二是ParamType的推导,T会作为ParamType的一部分,因此ParamType与T大多数情况下类型不一样,如下情况

template
void f(const T& param);

int x = 0;
f(x);

此时T == int,ParamType == const int&,这里的情况很好理解,传入的x是int类型的,T也推导出了int类型,但并非所有情况下他们两都一样,甚至T的推导类型结果还取决于ParamType,分三种情况

  1. ParamType是一个指针类型/引用类型,且非万能引用类型
  2. ParamType是万能引用类型
  3. ParamType是值类型

具体分析这三种情况下的推导情况

Case 1:ParamType是一个指针类型/引用类型,且非万能引用类型

规则:如果传递进来的expr为引用,则T推导为expr类型去除引用部分

For example:

template
void f(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);   // T is int, param's type is int&
f(cx);  // T is const int,param's type is const int&
f(rx);  // T is const int,param's type is const int&

此时如果我们给ParamType加上const修饰,不会影响到最终推导出来的ParamType得类型,而只是把const从T的推导结果中去除了

Case 2:ParamType是万能引用

规则:如果expr是左值则T和ParamType均推断为左值引用,如果expr是个右值引用则规则等同Case1

For example:

template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);    // x is lvalue => case2 => T is int&, param's type is int& && = int&
f(cx);   // cx is lvalue => case2 => T is const int&,param's type is const int& && = const int&
f(rx);   // rx is lvalue => case2 => T is const int&,param's type is const int& && = const int&
f(27);   // 27 is rvalue => case1 => remove refercent => T is const int,param's type is const int&&

Case 3: ParamType是值传递

规则:T推断为去除修饰符的expr类型(包括引用/const/volatile……)

For example:

template<typename T>
void f(T param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);     // T is int, param's type is int
f(cx);    // T is int, param's type is int
f(rx);    // T is int, param's type is int

//special
const char* const ptr = "123";
f(ptr);    // T is const char*, 此时只去掉了一个const,即f中的指针可以修改指向的对象,但是对象的内存还是不能被修改

这里要注意指针的处理逻辑

Array Arguments

特殊规则For example:

template<typename T>
void f(T param);

template<typename T>
void f2(T& param);

const char name[] = "J. P. Briggs";

f(name);    // T is const char*
f2(name);    // T is const char [13]

利用此原理可以在在传递数组指针进入其它函数后依然可以获取到数组元素个数(不然sizeof只能取到退化后的指针的长度)

template<typename T, std::size_t N>                 // see info
constexpr std::size_t arraySize(T (&)[N]) noexcept  // below on
{                                                   // constexpr
  return N;                                         // and
}                                                   // noexcept

参考自: EffectiveModernCppChinese

你可能感兴趣的:(c++,算法,数据结构,c++)