条款1:理解模板型别推导

假设有以下模板

1
2
3
template<typename T>
void func(ParamType i)
{}

ParamType为以下模式时,推导的结果符合预期

  • T&
  • T*
  • const T&

1
2
3
4
5
int p;
func(p); //ParamType = T& = int&
int *w;
func(w); //ParamType = T* = int*
func(&p); //ParamType = T* = int*

但如果ParamType = T&&,则会区分左值引用和右值引用

1
2
3
int p;
func(p); //ParamType = int&
func(12); //ParamType = int&&

测试

ParamType = T&, 所期望的是一个左值,如果传入一个右值则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
template<typename T>
void func(T& v)
{
std::cout << v << std::endl;
}

int main()
{
func(23);
return 0;
}

-------------------------------------

main.cpp:10:5: error: no matching function for call to 'func'
func(23);
^~~~
main.cpp:3:6: note: candidate function [with T = int] not viable: expects an
l-value for 1st argument
void func(T& v)
^

当使用万能引用时,则不会发生这种情况,为啥称为万能?我捉摸着是因为既可以匹配左值,又能匹配右值吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

template<typename T>
void func(T&& v)
{
std::cout << "&&" << v << std::endl;
}

int main()
{
func(23);
int i = 20;
func(i);
return 0;
}

退化

数组的类型是type[N],但是在将其传入模板时,会退化成type*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

template<typename T>
void func(T param) // 退化为char*
{
std::cout << *param << std::endl;
}

//char[12],这个推导是编译期推导,可以利用这个推导出数组的长度并当作constexpr使用
template<typename T, int N>
void func2(T(&param)[N])
{
std::cout << N << std::endl;
std::cout << param[0] << std::endl;
std::cout << *param << std::endl;
}

int main()
{
char p[] = "hello,world";
func(p);
func2(p);
return 0;
}

而函数也是如此,除非将其作为引用初始化,不过对于函数来说,并没有什么不同之处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

template<typename T>
void func(T param) //void(*)(int)
{
param(23);
(*param)(23);
}

template<typename T>
void func2(T& param) //void(&)(int)
{
param(24);
(*param)(24);
}

void say(int i)
{
std::cout << i << std::endl;
}

int main()
{
func(say);
func2(say);
return 0;
}

总结

  • &&能同时匹配左值和右值
  • 函数和数组在推导时有可能会退化为指针