最近闲来无事,在github上找打了facebook开发的基础库源码,在代码阅读的过程中,看到了很多新颖的template的语法。边看变查,同时记录一下,以免遗忘。
1. std::enable_if定义
通过跳转其定义的头文件,可以看到标准库中对该模板函数的定义如下。若_Test变量的取值为false则type未定义,否则type定义为_Ty。
// TEMPLATE CLASS enable_if
template<bool _Test,
class _Ty = void>
struct enable_if
{ // type is undefined for assumed !_Test
};
template<class _Ty>
struct enable_if<true, _Ty>
{ // type is _Ty for _Test
typedef _Ty type;
};
所以针对如下测试测试代码结果为:
int main()
{
// EnableCheck的类型为void,代码可以编过
using EnableCheck = std::enable_if<std::true_type::value>::type;
// EnableCheck的类型未定义,代码可以编不过
// using EnableCheck = std::enable_if<std::false_type::value>::type;
return 0;
}
2. template中匿名参数的使用
在template参数定义时,有时使用匿名参数来完成对部分参数条件的校验,只有校验通过才能够正常编译过,否则编译不过,如:
#include <iostream>
template<typename T>
struct NoneInt
{
static const bool value = true;
};
template<>
struct NoneInt<int>
{
static const bool value = false;
};
template <typename RetType, typename = std::enable_if<NoneInt<RetType>::value>::type>
RetType Func()
{
std::cout << "hello" << std::endl;
return 0;
}
int main()
{
//可编译过
Func<float>();
//可编译不过
//Func<int>();
return 0;
}
其中第二个 匿名模板参数就是对返回值的校验,只有返回值为int才能够编译通过。
3. 对模板定义特换方式的学习:
模板特化不只是对模板参数列表中的部分(或全部)参数类型指定,以下的方式同样是特化:
template <typename T>
Fuction;
template <typename RetType, typename... Args>
Fuction<RetType(Args...)>
{
}
4. 偏特化仅适用于模板类,不适用于模板函数。而且偏特化中明确指定类型,在构造函数的类型实参中仍需要指定,除非在全模板中有指定默认参数。如下
template <typename E, typename Enabled = void>
struct IsEnumUnderlying : std::false_type
{
};
template <typename E>
struct IsEnumUnderlying<E, typename std::enable_if<std::is_enum<E>::value>::type>
: std::integral_constant<bool, std::is_signed<typename std::underlying_type<E>::type>::value>
{
};
void TestDefaultParam()
{
enum EnumType {};
struct NonEnum {};
// 调用偏特化版本
IsEnumUnderlying<EnumType> a1;
// 调用全定义版本
IsEnumUnderlying<NonEnum> a2;
}
查看std::enable_if的定义可知,其仅对模板常数取值为true有Type定义,在模板推导时若模板常数取值为false将导致匹配失败。这种方式可用于对前置模板实参的条件限定,即只有同时匹配类型且满足限定条件时才能够完全匹配该模板定义。另外,类模板的构造函数不进行模板形参的类型推导。
5. 模板变参列表的使用
如下函数定义,第一个参数为函数指针,第二个参数为变参列表。在FunctionInvoke中调用该函数,通过forward将参数转发给对应的函数。标准库中的make_shared和thread的构造函数,都是采用与FunctionInvoke类型的定义方式。
template <typename F, typename... Args>
void FunctionInvoke(F f, Args... args)
{
f(std::forward<Args>(args)...);
}
void printHello(int a)
{
cout << "Hello" << endl;
}
void TestInvoker()
{
FunctionInvoke(&printHello, 10);
}
6. 常量定义
template <typename T>
struct IsInt
{
constexpr static bool value = false;
};
template <>
struct IsInt<int>
{
constexpr static bool value = true;
};
// 简化模板的使用
template <typename U>
constexpr bool IsInt_v = IsInt<U>::value;
void Test()
{
//调用方式如下,输出为false
cout << IsInt_v<float> << endl;
}
7. 移动语义和完美转发
std::move生成的是右值,std::forward是将原来的类型,转发给下一个参数使用(赋值或接口调用)。