一、SFINAE的技巧
SFINAE,Substitution Failure Is Not An Error。这个在前边的模板元编程中分析过,这里不再赘述,本文只对两个类似实现技巧进行分析来促进在实际工程中的应用。一个是Muduo库的,一个是使用标准库的接口实现的。
此处向陈硕先生致敬。
二、Muduo库中的例子
先上源码:
#include <iostream>
namespace detail
{
// This doesn't detect inherited member functions!
// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
template<typename T>
struct has_no_destroy
{
template <typename C> static char test(decltype(&C::no_destroy));
template <typename C> static int32_t test(...);
const static bool value = sizeof(test<T>(0)) == 1;
};
} // namespace detail
template <typename T>
static void init(T t)
{
T* value_ = new T();
if (!detail::has_no_destroy<T>::value)
{
std::cout<<"is ok"<<t<<std::endl;
//::atexit(destroy);
}
}
int main()
{
int num = 200;
init<int>(num);
return 0;
}
这里为了方便增加了测试的程序。
这段代码是muduo库中的一个单例控制方式,采用了前面提到的SFINAE技术。程序很简单首先看模板类has_no_destroy,它内部有两个test模板函数的重载(不明白函数重载可以查查资料),还有一个静态常量值,布尔类型的(在c++20中可以考虑inline)。这个变量值是通过sizeof这两个模板函数的返回值来判断其为char或int32_t来确定真假。也就是说,如果是decltype(&C::no_destroy)存在,即C类中包含函数no_destroy,那么模板重载匹配成功,即sizeof的值为其返回值char(1==1成立),否则为int32_t(4==1不成立)。
其实这种技术在前面反复分析过,这里之所以再拿出来说事儿,目的只有一个,和实际工程结合,再给大家一个更深的印象。
三、c++11后的方法
在c++11后,在前面提到了下面的方法:
template<typename T>
auto len (T const&& t) -> decltype((void)(t.size()) , T::size_type)//也可以使用std::enable_if,修改一下表达式即可
{
return t.size();
}
这里面有两个问题,一个是(void)(t.size()),强制转成void,是为了避免用户重载逗号运算符导致的其它行为可能性(即消除不确定性)。另外一个就是逗号表达式的作用了最后一个式子为真正的预期返回类型,但有一个前提,前面所有的逗号表达式的内容均为有效,而如果t中没有size()函数,则会编译失败。
其实如果到了c++20,就可以直接使用Concepts技术。
template<typename T>
concept func_ask = requires(T t)
{
t.no_destroy();
};
整体上来说,更简单明了。
四、总结
本篇文章属于温故而知新的章节,和前面的文章相互印证就会更深刻的反思。然后能不能更好的掌握这些技术并应用,就打开了一个门,后面就全靠自己了。