1. 什么是SFINAE

在C++中有很多的编程技巧(Trick), SFINAE就是其中一种, 他的全义可以翻译为”匹配失败并不是一个错误(Substitution failure is not an error)“. 简单来说他就是专门利用编译器匹配失败的一种技巧.

2. 案例

比如我们想实现一个通用的函数叫AnyToString, 他可以实现任意类型的数据转成字符串:

 template<typename ValueType>
char* AnyToString(const ValueType& value);

我们更希望这个函数能检查ValueType类型自己有没有ToString方法, 如果有就直接调用, 没有的话就采取通用的处理方案. 但是C++没有反射机制, 不能像C#那样通过TypeInfo来检查, 更没有像Java那样纯粹的OOP,从最基类就定义了ToString方法,下面的子类只用负责重载。

所以我们希望能有一种方法能让C++也能检查某个类型是否定义了某个成员函数, 这就可以用到SFINAE.

3. 解决方案

C++的模板匹配有个特点, 编译器始终会寻找类型匹配最精确的模板. 当然并不一定所有的模板都能匹配, 一旦有某个模板匹配不成功, 编译器会自动尝试别的候选模板, 要是所有的都不成功那编译器就匹配失败, 有的时候我们想故意跳过某些精确度高模板匹配, 而使用精确度低的模板, 这个时候就可以利用SFINAE故意让编译器匹配失败. 回到案例, 我们希望检查一个类型是否有ToString方法, 例如:

class A { char* ToString(); };

class B { };

这时我们在代码里面写A::ToString, 自然没有什么问题, 但是如果写B::ToString的话编译将告诉你找不到这个符号. 我们可以利用这个错误来跳过某些模板的匹配, 而使得别的模板可以得到匹配. 例如以下代码:

 template<typename ClassType>
struct HasToStringFunction {
typedef struct { char[]; } Yes;
typedef struct { char[]; } No; template<typename FooType, char* (FooType::*)()>
struct FuncMatcher; template<typename FooType>
static Yes Tester(FuncMatcher<FooType, &FooType::ToString>*); template<typename FooType>
static No Tester(...); enum {
Result = sizeof(Tester<ClassType>(NULL)) == sizeof(Yes)
};
}; bool a_has_tostring = HasToStringFunction<A>::Result; // True
bool b_has_tostring = HasToStringFunction<B>::Result; // False

这里有两个Tester方法, 第一个的匹配精度高于第二个的.

当编译器解析Tester<ClassType>(NULL)的时候, 编译器首先会尝试用ClassType以及他的一个ClassType::ToString方法去实例化一个FuncMatcher类型来匹配第一个Tester函数. 对于A来说, 这是能通过的.

但是对于B来说, 因为其没有ToString方法, 所以不能用B以及不存在的B::ToString来实例化FuncMatcher.

这个时候编译器实际上就已经发现错误了, 但是根据SFINAE原则这个只能算是模板匹配失败, 不能算错误, 所以编译器会跳过这次对FuncMatcher的匹配. 但是跳过了以后也就没有别的匹配了, 所以整个第一个Tester来说对B都是不能匹配成功的, 这个时候优先级比较低的第二个Tester自然就能匹配上了. 我们就可以利用这一点来实现我们最开始的想要AnyToString方法:

template<bool>
struct AnyToStringAdviser; template<>
struct AnyToStringAdviser<true> {
template<typename ValueType>
static char* ToString(const ValueType& value) {
return value.ToString();
}
} template<>
struct AnyToStringAdviser<false> {
template<typename ValueType>
static char* ToString(const ValueType& value) {
/* Generic process */
}
} template<typename ValueType>
char* AnyToString(const ValueType& value) {
return AnyToStringAdviser<HasToStringFunction<ValueType>::Result >::ToString(value);
}

4. 再写一个常用的使用了该方法的traits工具类

 template <typename T>
struct is_class{
typedef char __one__;
typedef struct{ char[]; } __two__; template <typename U>
static __one__ test(int U::*){ } template <typename U>
static __two__ test(...){ } const static bool value = (sizeof(test<T>(NULL)) == sizeof(__one__));
};

C++ SFINAE的更多相关文章

  1. C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

    本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...

  2. SFINAE简单实例

    SFINAE(Substitution failure is not an error),是C++11以来推出的一个重要概念,这里,只是简单举一个例子,可能会有人需要. // 添加 scalar nu ...

  3. C++模板进阶指南:SFINAE

    C++模板进阶指南:SFINAE 空明流转(https://zhuanlan.zhihu.com/p/21314708) SFINAE可以说是C++模板进阶的门槛之一,如果选择一个论题来测试对C++模 ...

  4. SFINAE and enable_if

    There's an interesting issue one has to consider when mixing function overloading with templates in ...

  5. SFINAE 与 type_traits

    SFINAE 与 type_traits SFINAE 替换失败不是错误 (Substitution Failure Is Not An Error),此特性被用于模板元编程. 在函数模板的重载决议中 ...

  6. c++11-17 模板核心知识(八)—— enable_if<>与SFINAE

    引子 使用enable_if<>禁用模板 enable_if<>实例 使用Concepts简化enable_if<> SFINAE (Substitution Fa ...

  7. SFINAE 模板替换失败而非报错的应用

    体会这一个例子,检查是否是一个类:P187

  8. Google C++ Style Guide

    Background C++ is one of the main development languages used by many of Google's open-source project ...

  9. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

随机推荐

  1. while (cin>>str)退出死循环

    今天在练习的时候突然发现了这个问题,百度之感觉还挺常见的,故记之! //题目描述 // //写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串. // //输入描述 : //输入一个 ...

  2. SQL存儲過程的調試方法

    1.在vs2010调试存储过程步骤如下:(要點:連接登陸賬號的權限必須是管理員,才能單步調試,否則只能直接執行存儲過程:[因此,此方式適合數據庫和vs裝在同一台電腦上]) 1.1首先,打开vs,点击 ...

  3. 写好Hive 程序的五个提示

    转自http://www.alidata.org/archives/622 使用Hive可以高效而又快速地编写复杂的MapReduce查询逻辑.但是某些情况下,因为不熟悉数据特性,或没有遵循Hive的 ...

  4. 通过ajax提交form表单

    $.ajax({ url : 'deliveryWarrant/update.do', data : $('#myform').serialize(), type : "POST" ...

  5. iOS 检测有没有安装其它应用 和ios9下要注意的地方

    UIApplication *app = [UIApplication sharedApplication]; NSURL *url = [NSURL URLWithString:@"Tri ...

  6. cocos2d 设置按钮不可用

    需要两步设置按钮变灰,然后不可点击 btnBuy.setBright(false); btnBuy.setTouchEnabled(false); 或者直接不显示按钮 btnBuy.setEnable ...

  7. Spring.Scheduling.Quartz的使用

    最近因使用Spring.Net框架而接触.了解到其与Quartz.Net的集成,即Spring.Scheduling.Quartz模块. Spring通过对Quartz.Net的封装,采用了sprin ...

  8. HDU1151Air Raid(二分图的最大匹配)

    题目大意: 有一个城镇,它的所有街道都是单行的,并且每条街道都是和两个路口相连.同时已知街道不会形成回路. 你的任务是编写程序求最小数量的伞兵,这些伞兵可以访问(visit)所有的路口.

  9. hdoj 5358 First One

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5358 一开始一直以为是一道数学题,在找有什么规律化简Log2(S(i,j)),结束了以后才造  ⌊lo ...

  10. Spring REST实践之Versioning,Paging和Sorting

    Versioning 为适应需求的变化以及兼容已有的API,需要创建新版本的API,一般有四种流行的版本化API的方法: URI版本化 URI参数版本化 Accept header版本化 自定义hea ...