有关SFINAE的一个小应用

来源:互联网 发布:软件测试社区 编辑:程序博客网 时间:2024/06/11 07:19

    之前在实现一个类模板时遇到一个问题:如何判断一个类型是否重载了operator<(),就凭我(对c++的理解仅限于C++ primer)的技术解决不了,于是求助互联网,得到了一个方法——SFINAE

1. 定义   

    SFINAE 全称 Substitution Failure Is Not An Error, 直译为 匹配失败不是错误或者说不是编译时错误。 在《Extended STL, Volume 1: Collections and Iterators》中13章可以看到。

2. 解释

    先来看c++Primer中的有关函数模板的匹配:

  • 对于一个调用,其候选函数包括所有的模板实参推断成功的函数模板实例。
  • 候选的函数模板总是可行的,因为模板实参推断会排除任何不可行的模板。
  • 可行函数(模板与非模板)按类型转换来排序,可用于函数模板调用的类型转换是非常有限的(a.顶层const被忽略 b.非const对象的引用(或指针)可转换为construction对象的应用(或指针) c.数组或函数指针的转换)。
  • 如果恰有一个函数提供比任何其他函数都更好的匹配,则选择此函数。 但如果有多个函数提供同样好的匹配,则:

——同样好的函数中只有一个是非模板函数,则选择此函数。

——同样好的函数中没有非模板函数,而有多个函数模板,且其中一个模板比其他模板更特例化,则选择此模板。

——否则调用有歧义。

(我想只要读过C++Primer 大概都能懂吧-_-||)

    再来看看一个(别人写的)简单例子,我觉得可以很好的表示SFINAE的意思。

#include<iostream>void print(int rnval){std::cout<<"void print(int)"<<std::endl;}template<typename T>void print(T rnt, typename T::value_type* rndummy_ptr = nullptr){std::cout<<"template<typename T> void print(...)"<<std::endl;}int main(){short rnval1 = 0;print(rnval1);return 0;}

    执行结果中print调用非模板的那个。 问题在于编译器为什么会放弃用short去实例化print模板而去提升short为int去执行第一个print 。

你可能会说“你前面不是写了函数匹配的顺序了吗?结果不是很自然么?”, 事实上,由于实参传递给形参时发生类型转换,所以不会是精确匹配,只能是可行函数,然而如果print模板实例化print<short>,那此时就是精确匹配了,但是该实例print<short>的第二个形参却会发生错误,所以概括来说就是编译器此时 宁可对有问题的类型不考虑函数的重载也不要产生一个编译时错误。

    换句话说: 编译器在辨认函数模板时,假如有一个特化会导致编译时错误,只要还有别的选择可以被选择,那就无视这个特化错误而去选择另外的可选选择,这就是SFINAE技术。

    再看一个例子:

class test{};int main(){test a;print(a);return 0;}
这段代码执行后会报错,而且编译器给出的提示是: “note:   no known conversion for argument 1 from 'test' to 'int'”,这就验证了上面描述的编译器匹配过程。

我这个例子只是SFINAE的最基础应用,事实上该技术的应用非常灵活,就我的理解,我把SFINAE理解成合法的试错场。就利用它,我可以解决上面提到的问题:判断一个类型是否重载了operator<() 。

3. 应用

直接上代码:

// judge if a class has reloaded operator< or not#ifndef HASOPERATORLESS_H#define HASOPERATORLESS_Htemplate<typename T>struct HasOperatorLess{template<typename T1> static char check(decltype(T1() < T1())*);template<typename T2> static int check(...);static constexpr bool value = (sizeof check<T>(0) == sizeof(char));};#endif

该实现非常简单,也很容易理解,decltype运算符计算T1() < T1() 如果失败则匹配 check(...).

value为true 则该类型重载了operator<(),为false则没重载。


本人小白一个,有错误欢迎批评指点大笑

学习自http://blog.csdn.net/cd2108006026/article/details/7999948

原创粉丝点击