类型推导之auto-template-decltype
# auto-template-decltype三者在类型推导时的区别
参考effective modern c++
# template
template推导T的类型时,有一个规律,就是模板函数传入的参数类型
和调用函数时传入的类型
匹配一致,并且T尽量简单。
其中左值和左值引用认为是一致的,右值和右值引用认为是一致的。
具体来看三种情况:
# 1.函数模板参数为普通引用(左值引用)时,整个表达式匹配为左值引用
template<typename T>
f(T& param);
int x=27;
const int cx=x;
const int & rx=cx;
// 模板函数传入的参数类型为`T& param`, 让其和实际调用时传入的类型一致
f(x); //实际传入的是int,需要让`T& param`匹配int或int&,那么T应是int, 模板传入的`T& param`即为int& param
f(cx); //实际传入的是const int, 需要让`T& param`匹配const int&, 那么T应是const int,模板传入的`T & param`即为const int &
f(rx); //实际传入的是const int&, 需要让`T& param`匹配const int&, 那么T应是const int,模板传入的`T & param`即为const int &
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.函数模板参数为通用引用"T&& param"时, 整个表达式匹配为左值引用或右值引用
- 当传入左值或左值引用时,该表达式整体匹配为左值引用。
- 假设传入类型为左值或左值引用(如int或int&),T会被推导为int&, "T&& param"整体即为int& && param, 折叠为int& param
- 当传入右值时,该表达式整体匹配为右值引用。
- 假设传入类型为右值int&&,T会被推导为int,整体为int&& param, 无需折叠。
- 注意右值引用变量本身也是左值,因此如果存在两层函数调用,第一层用了T&&, 且传参为右值,则第一层函数内参数被推导为右值引用,第二层函数传参时需要讲参数声明为T&&,且调用时加上std::forward才能使二层函数内的参数仍推导为右值引用。
- std::forward的原理是第一层函数调用时如果参数为左值,则第一层的T被推导为左值引用,int&;第一层如果是右值,则T被推导为int。故forward的操作就是将参数做一个
static_cast<T &&>(param)
,那么左值引用的情况int&加上&&仍折叠为左值引用,右值引用的情况第一层T是int,故cast之后仍为右值引用。
template<typename T>
void f(T&& param); //param现在是⼀个通⽤引⽤类型
int x=27;
const int cx=x;
const int & rx=cx;
// 模板函数传入的参数类型为`T&& param`, 让其和实际调用时传入的类型一致
f(x); //实际传入的是int, 需要让`T&& param`匹配int或int&, 那么T应是int&, 模板传入的`T&& param`即为`int& && param`, 折叠为int&。 这里如果把T推导为int, 则`T&& param`就是int&& param,与实际传入的左值int不匹配。
f(cx); //实际传入的是const int, 需要让`T&& param`匹配const int, 那么T应是const int&, 模板传入的`T&& param`即为`const int& && param`, 折叠为const int&
f(rx); //实际传入的是const int&, 需要让`T&& param`匹配const int, 那么T应是const int&, 模板传入的`T&& param`即为`const int& && param`, 折叠为const int&
f(27); //实际传入的是int&&, 需要让`T&& param`匹配int&&, 那么T应是int, 模板传入的`T&& param`即为int&&
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.函数模板参数为非引用(按值传递)时,
因为是按值传递,所以推导T的类型时会剥离掉 const、volatile、& 等约束
template<typename T>
f(T param);
int x=27; //如之前⼀样
const int cx=x; //如之前⼀样
const int & rx=cx; //如之前⼀样
f(x); //T和param都是int
f(cx); //T和param都是int
f(rx); //T和param都是int
const char* const ptr = " Fun with pointers"; //第二个const表示ptr自身是⼀个常量指针(不能修改ptr指向谁),第一个const表示其指向的对象是常量
// 这种情况下,值传递会把第二个const属性(ptr自身)去掉,留下第一个,即param是const char*
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# auto
- auto的作用规则与上面的template基本一样, auto就仿佛是上面的T
- auto与template的区别在于,auto可以推导出出std::initializer_list, 但是template遇到出std::initializer_list会报错。
比如
int x=27; //如之前⼀样
const int cx=x; //如之前⼀样
const int & rx=cx; //如之前⼀样
auto & a1 = x; // auto为int, a1为int &
auto && a2 = cx; //auto 为const int &, a2为 const int &
auto && a3 = 27; //auto 为int, a3为 int &&
// 让左边和右边的类型一致, 但是上面最后一条规则(按值传递)会自动剥离const和引用,所以:
auto a4 = rx; //a为int, 剥离了const和&
decltype(auto) a5 = rx; //这是不剥离的方式,a5为const int &,
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
auto a = {5}; //此时a的类型为std::initializer_list<int>
template<typename T>
void f(T param);
f({11,23,9}); //错误!不能推导出T
1
2
3
4
5
6
2
3
4
5
6
# decltype
decltype(expression)就是推导出表达式的类型。直观易理解,其会保留const、volatile等类型,且当expression为右值引用变量时,虽然右值引用变量本身是左值,但是decltype推导出来的仍是右值引用。
const int i=0; //decltype(i)是const int
bool f(const Widget& w);//decltype(f)是bool(const Widget&)
vector<int> v; //decltype(v)是vector<int>, decltype(v[0])是int&
int && a = 5;
auto b1 = 7; //b为int
decltype(a) b2 = 6; //b为int&&
decltype(auto) b3 = 8; //b为int
decltype(auto) b4 = std::move(9); //b为int&&
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
decltype(auto)的使用场景.
template<typename Container,typename Index> //C++ 14版本
auto authAndAccess(Container& c,Index i)
{
authenticateUser();
return c[i]; //这里c[i]类型为int&,但是返回值auto会剥离掉&,因此返回的是int
}
std::deque<int> d;
authAndAccess(d,5)=10; //认证⽤⼾,返回d[5],由于返回的是int临时值,无法把10赋值给它, ⽆法通过编译器!
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
把上面的authAndAccess改成这样就可以了:
template<typename Container,typename Index>
decltype(auto) authAndAccess(Container& c,Index i)
{
authenticateUser();
return c[i]; //decltype(auto)表示用decltype的规则推导返回值类型
}
1
2
3
4
5
6
2
3
4
5
6
decltype(auto) 可以这样理解:auto说明符表⽰这个类型将会被推导,decltype说明decltype的规则将会应用到这个推导过程中。
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54