C++ SFINAE
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的更多相关文章
- C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE
本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...
- SFINAE简单实例
SFINAE(Substitution failure is not an error),是C++11以来推出的一个重要概念,这里,只是简单举一个例子,可能会有人需要. // 添加 scalar nu ...
- C++模板进阶指南:SFINAE
C++模板进阶指南:SFINAE 空明流转(https://zhuanlan.zhihu.com/p/21314708) SFINAE可以说是C++模板进阶的门槛之一,如果选择一个论题来测试对C++模 ...
- SFINAE and enable_if
There's an interesting issue one has to consider when mixing function overloading with templates in ...
- SFINAE 与 type_traits
SFINAE 与 type_traits SFINAE 替换失败不是错误 (Substitution Failure Is Not An Error),此特性被用于模板元编程. 在函数模板的重载决议中 ...
- c++11-17 模板核心知识(八)—— enable_if<>与SFINAE
引子 使用enable_if<>禁用模板 enable_if<>实例 使用Concepts简化enable_if<> SFINAE (Substitution Fa ...
- SFINAE 模板替换失败而非报错的应用
体会这一个例子,检查是否是一个类:P187
- Google C++ Style Guide
Background C++ is one of the main development languages used by many of Google's open-source project ...
- C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)
因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...
随机推荐
- linq数据使用
取出数据库满足条件的记录的ID,把值放到list中 ) { int userid = Convert.ToInt32(Request.Cookies["id"].Value); v ...
- javascript对象事件绑定方法
javascript对象事件绑定方法 今天在做对象事件绑定的过程中出现了一点异外情况,由于事件方法是由参数传过来的,需要将当前对象call过去,方便方法体里直接调用this 错误写法 obj.oncl ...
- arp spoofing
Today our tutorial will talk about Kali Linux Man in the Middle Attack. How to perform man in the mi ...
- 使用HttpClient实现文件的上传下载
1 HTTP HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源. 虽然在 JDK 的 java.net ...
- 《Java数据结构与算法》笔记-CH4-6优先级队列
/** * 优先级队列 * 效率:插入O(n),删除O(1).第12章介绍如何通过堆来改进insert时间 */ class PriorityQueue { private int maxSize; ...
- 【FFT】专题总结
学了若干天终于学(bei)会了传说中的法法塔 感觉也没那么难用嘛 fft快速傅里叶变换 在大表课件上写就是解决高精乘的工具 其实很有理有据 fft就是用复数的折半引理优化两个多项式相乘的高端东西 他能 ...
- MoveTo和MoveBy
cc.MoveTo是“移动到这里",而cc.MoveBy则是“相对于之前点再移动”,通俗一点就是说这里需要两个坐标pos1(x1,y1),pos2(x2,y2). 如果是cc.MoveTo的 ...
- 前端复习-03-接上面ajax跨域问题的解决与探索
废话不多少 ..我估计一万个人都搞不清楚 什么是跨域 然后就被这堵墙无情的挡住了..我尝试了很多办法解决这个问题.后来再慕课网上的一个老师的ppt那里看到一张图 我觉得 能记住这张图的话 应该就算是深 ...
- 第三百五十七天 how can I 坚持
502是我对你没有爱的意思吗?为什么要要这样啊,好绝情. 明天要去加班,今晚回来也好晚了,晚上回来都有点精神恍惚了. 他们要聚会,本来要想去樱木花道来,哎. 后天..什么都没学. .. .. .. 准 ...
- jxse2.6在jdk8下,JxtaMulticastSocket存在的问题
JxtaMulticastSocket覆写了java.net.MulticastSocket的bind方法: @Override public void bind(SocketAddress addr ...