条款1: 理解模板型别推导
有如下模板伪代码:
template<typename T>
void f(ParamType param);
f(expr);
T的型别推导,不仅仅依赖实参的型别,还依赖ParamType的形式。具体有:
- ParamType 具有指针或引用型别(非万能引用)。
- ParamType 是一个万能引用。
- ParamType 既非指针也非引用。
情况1 ParamType是指针或引用
即如下(可能的):
template<typename T>
void f(T& param);
型别推导会这样做:
-
若expr具有引用型别,先将引用部分忽略。
-
尔后,对expr的型别和ParamType的型别执行模式匹配,来决定T的型别。
情况2 ParamType是万能引用
即如下:
template<typename T>
void f(T&& param);
注意,形参声明方式写作T&&
。
如果expr是左值,T和ParamType都会被推导为左值引用。
如果expr是右值,则使用情况1中的规则。
情况3 ParamType既非指针也非引用
即如下:
template<typename T>
void f(T param);
导出规则为:
- 若expr具有引用型别,则忽略其引用部分。
- 之后,若expr是个const对象,也忽略之。
Note
这里说的const对象是指顶层const,即如果是const char *const
,那么会推导成const char *
。
数组实参
由于数组到指针的退化规则的存在,如果模板函数如:
template<typename T>
void f(T param);
那么将一个数组(比如 const char name[] = "diwen"
)传递给它时,T会被推导成 const char *
。
可以将形参声明成数组的引用,方法是将模板改成:
template<typename T>
void f(T& param);
T将推导出实际的数组型别,包含数组的尺寸。
利用这一能力,可以创造一个模板,用来推导出数组含有的元素个数:
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept
{
return N;
}