Template Type Deduction

C++模板类型推导

对于如下的C++模板类型推导

template<typename T>
void f(ParamType param);
f(expr);// 根据expr推导T和ParamType

要分三钟情况:

Case 1 ParamType是引用或指针,且不是Universal Reference

  • 首先,如果expr是引用类型,则C++模板类型推导时会忽略参数expr的reference-ness。
  • 然后,对ParamType的推导决定对T的推导

正常情况下,expr的constness会被保留,但是下面代码中函数f(const T& param)参数型中已经有const关键字时,则T只就不再含有constness。

#include <iostream>
using namespace std;
template<typename T>
void f(const T& param) // 此时T被推导为int, param的类型为const int &
{
	cout << param << endl;
	T tmp = 2019; // 正常运行
	tmp += 1;
	cout << tmp << endl;
}

void g(T& param) // 此时T被推导为const int, param的类型为const int &
{
	cout << param << endl;
	T tmp = 2019; // error,无法编译
	tmp += 1;
	cout << tmp << endl;
}

int main()
{
	int x = 27; // as before
	const int& rx = x; // as before
	f(rx); // T is int
	g(rx); // T is const int
	return 0;
}

指针型的推导类似。

Case 2 ParamType是Universal Reference

Universal Reference翻译成通用引用?

这种情况,没那么明显直观了。模板类型的声明有点像右值引用(rvalue references, T&&)。但是当expr是左值(lvaue)时,推导行为有点“怪异”。

  • 如果expr是一个左值(lvalue),则T和ParamType都被推导为lvaue references。这很不常见。首先,在模板类型推导中,这是唯一一个把T推导为引用的场景。其次,尽管ParamType是用rvalue reference的语法来声明的,但是其推导出来的类型是lvalue reference。
  • 如果expr是一个rvalue,则应用普通的推导规则(如,Case 1的规则)

举例:

template<typename T>
void f(T&& param); // param is now a universal reference
int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // x is lvalue, so T is int&,
// param's type is also int&
f(cx); // cx is lvalue, so T is const int&,
// param's type is also const int&
f(rx); // rx is lvalue, so T is const int&,
// param's type is also const int&
f(27); // 27 is rvalue, so T is int,
// param's type is therefore int&&,右值(rvalue)

Case 3 ParamType既不是指针也不是引用

当ParamType即不是指针也不是引用时,则按值传递(pass-by-value):

template<typename T>
void f(T param); // param is now passed by value

这意味着无论传进来的是什么,param都是一份copy,一个全新的object。

  • 和之前一样,如果expr是引用类型,则忽略referenceness
  • 如果忽略过referenceness后,expr是const的,则也忽略constness。如果expr是volatile的,则也忽略。

因此:

int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int

注意尽管上面的cx和rx是const的,但param却不是const的。expr是const的,即不能修改,并不意味着它的copy也是不能修改的。
只有在按值传递时const(和volatile)才会被忽略。对于引用或指针类的parameters,constness是要保留的。

template<typename T>
void f(T param); // param is still passed by value
const char* const ptr = // ptr is const pointer to const object
"Fun with pointers";
f(ptr);

注意上面的代码中ptr是按值传递的,param的类型是const char*。

总结:

  • 在模板类型推导时,忽略reference-ness
  • 在推导universal references类型的paramterer时,lvalue类型的arguments要特殊对待
  • 当推导按值传递的parameters时,const和volatile类型的arguments 被当作non-const和non-volatile处理
  • 在模板类型推导时,数组或函数作为arguments时,它们会退化为pointers。(除非它们用于初始化引用)

参考:

Effective Modern C++

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值