这里是《C++ Templates 2th》的读书笔记。
SFINAE
为Substitution Failure Is Not An Error的简写,意味着“替换失败并不是个错误”。
假设我们现在有这样的代码:
|
|
[1]的功能是在buffer中放入repeat个字符c。
[2]的功能是将字符串c放入buffer中。
[3]的功能是给入迭代器begin和end,将其间所有数据放入buffer。
接下来我们看一下这个使用方法:
|
|
这里我们的本意是让其调用[1],往buffer里放10个ch。但是编译器会调用[3],因为[3]更符合参数为int, int
的情况。
这里我不想让这种情况发生,那么我们可以这样写:
|
|
这里就不得不介绍一下enable_if
和is_integral
了。
is_integral
只有一个模板参数T,并且有一个静态成员bool value
。当T为整数类型时,value
为true,否则为false。说白了他就是用来判断类型是否为整数的模板类。
enable_if
有两个模板参数数enable_if<bool, typename T = void>
,和一个类型别名using type = XXX
。如果bool为true,则enable_if
的type
成员为T,否则不存在这个成员。
那么再回来看这个模板。当我们传入两个整型的时候,std::_is_integral<Iter>::value
将会返回true,我们对他取反变为false,这样std::enable_if<false>
就不存在type
这个成员,那么这个模板显然就是有问题的,编译器就不会去匹配这个模板,但是他也不会报错,而是去寻找下一个函数看看能不能匹配。显然,这会匹配到void Append(char, size_t)
。
这就是SFINAE
,当出现了无意义或者有问题的模板匹配时,编译器不会报错,而是自动忽略,所以这规则叫做“替换失败不是个错误”。
类型萃取
类型萃取是一个利用模板特性而实现的功能(一般是利用特化),其典型的一个用法就是从指针类型种将原类型抽出,即我们要实现这样一个功能:
|
|
如何实现呢,请看代码:
|
|
这里使用了模板的偏特化。[1]是模板的通用形式,对于任意的类型T,让type
为
T,即不改变传入的类型。
[2]是模板的偏特化,注意strip_point<U*>
,当我们传入strip_point<int*>
时,U*
会被推断为int*
,这样U
就是int
了,所以这样就剥离了一层指针。
这种从某种混合类型种抽取(萃取)出某种特定类型的功能就是类型萃取。
我们可以这样来让此功能变得更方便使用:
|
|
类型萃取广泛地运用在标准库中,尤其是type_triats
头文件里。上面strip_point
的功能其实就是标准库中remove_pointer
的功能。
enable_if的实现
接下来我们简单实现一个enable_if
:
|
|
很简单吧,就是根据enable_if
的定义,使用偏特化就可以做出来了。
通用函数
我们可以通过这种形式给模板一个通用函数:
|
|
你没看错,参数就是三个点。这种函数总会匹配所有调用,但是其匹配情况是重载函数中最差的,不到万不得已找不到其他函数时编译器是不会调用这个函数的。