C++11 元编程(meta-programming)判断T是否有==操作符
2015-12-09 09:43
489 查看
前几天看了《C++11之美》受到一些启发,想到可以通过判断一个类型是否有指定的操作符(比如==,>=)。
基本的原理与文中的差不多,利用SFINAE原则,通过返回类型后置来推断表达式的类型,推断的过程中利用
如果通过
如果通过
我们最后判断实例化的
在上面代码中,推导
第一种是真接按常用的
第一种方式可以用于判断基本数据类型和class类型。
对于基本数据类型(比如int),因为没有成员函数,所以第二种方式对于基本类型返回的肯定是false.无法用这种方式判断基本数据类型是否有==操作符,只适用于class类型。
基于上面这个元函数的原理,我们还可以继续写出其他操作符的判断函数,比如
下面是完整的代码
下面是
看来还是对STL提供的元函数不熟悉,否则如果早想到看看
下面是gcc的
基本的原理与文中的差不多,利用SFINAE原则,通过返回类型后置来推断表达式的类型,推断的过程中利用
declval,它可以获取类型的右值引用,以便来调用
==操作符,这个过程是在编译期完成的。
如果通过
==操作符比较
declval的右值引用成功了,则会继续推断逗号表达式的类型,最终推断的函数返回类型为
bool;
如果通过
==操作符比较
declval的右值引用失败了,则推断失败,编译器会选择优先级最低的
test(...)函数,它的返回类型为
void。
我们最后判断实例化的
test<T>(0)的返回值是否为
bool,可以知道类型T是否存在
==操作符。
template <typename T> struct has_equal_operator{ template<typename U> static auto test(int)-> decltype(declval<U>()==declval<U>()); //template<typename U> static auto test(int)-> decltype(declval<U>().operator==(declval<U>())); template<typename U> static void test(...); enum{value=std::is_same<decltype(test<T>(0)), bool>::value}; };
在上面代码中,推导
test(int)返回类型的表达式是由执行
==操作符比较两个
declval获取的右值引用来实现的。有两种方式
declval<U>()==declval<U>()和
declval<U>().operator==(declval<U>())
第一种是真接按常用的
==操作符用法写的
==表达式,第二种则是把操作符
==作为一个类成员函数来调用。两种表达式判断是有区别的:
第一种方式可以用于判断基本数据类型和class类型。
对于基本数据类型(比如int),因为没有成员函数,所以第二种方式对于基本类型返回的肯定是false.无法用这种方式判断基本数据类型是否有==操作符,只适用于class类型。
基于上面这个元函数的原理,我们还可以继续写出其他操作符的判断函数,比如
>,
*操作符。
下面是完整的代码
#include <iostream> #include <type_traits> using namespace std; struct test_classA{ int a; virtual bool operator==(const test_classA&v){ return a==v.a; } virtual ~test_classA()=default; }; struct test_classB:test_classA{ }; struct test_classC:test_classB{ }; template <typename T> struct has_equal_operator{ template<typename U> static auto test(int)-> decltype(declval<U>()==declval<T>()); //template<typename U> static auto test(int)-> decltype(declval<U>().operator==(declval<T>())); template<typename U> static void test(...); enum{value=std::is_same<decltype(test<T>(0)), bool>::value}; //通过判断test<T>(0)返回值是否为bool来判断是否有==操作符 }; template <typename T> struct has_asterisk_operator{ template<typename U> static auto test(int)-> decltype(*declval<U>()); template<typename U> static void test(...); enum{value=std::is_reference<decltype(test<T>(0))>::value}; //通过判断test<T>(0)返回值是否为引用来判断是否有*操作符 }; template <typename T> struct has_gt_operator{ template<typename U> static auto test(int)-> decltype(declval<U>()>declval<U>()); template<typename U> static void test(...); enum{value=std::is_same<decltype(test<T>(0)), bool>::value}; //通过判断test<T>(0)返回值是否为bool来判断是否有>操作符 }; int main() { cout<<"int has operator> :"<<has_gt_operator<int>::value<<endl; cout<<"int* has operator> :"<<has_gt_operator<int*>::value<<endl; cout<<"test_class has operator> :"<<has_gt_operator<test_classA>::value<<endl; cout<<"int has operator* :"<<has_asterisk_operator<int>::value<<endl; cout<<"int* has operator* :"<<has_asterisk_operator<int*>::value<<endl; cout<<"int has operator== :"<<has_equal_operator<int>::value<<endl; cout<<"test_class has operator== :"<<has_equal_operator<test_classA>::value<<endl; cout<<"test_classC has operator ==:"<<has_equal_operator<test_classC>::value<<endl; cout<<"hasequal<double> has operator== :"<<has_equal_operator<has_equal_operator<double>>::value<<endl; }
下面是
has_equal_operator的使用场景的例子:
/* 判断obj1,obj2是否相等 * 如果K有==操作符则使用==比较版本,否则使用default_equals函数进行二进制比较 */ template<typename _K=K> typename std::enable_if<!has_equal_operator<_K>::value,bool>::type equals(const _K &obj1, const _K &obj2)const { return 0 == default_equals(&obj1, &obj2, sizeof(_K)); } template<typename _K=K> typename std::enable_if<has_equal_operator<_K>::value,bool>::type equals(const _K &obj1, const _K &obj2)const { return obj1==obj2; }
后记:
本文在C++论坛发出后,经网友akirya提醒才知道std::is_assignable其实就是采用本文类似的原理。
看来还是对STL提供的元函数不熟悉,否则如果早想到看看
std::is_assignable的源码,就不会花这么时间了。
下面是gcc的
std::is_assignable相关源码:
template<typename _Tp, typename _Up> class __is_assignable_helper { template<typename _Tp1, typename _Up1, typename = decltype(declval<_Tp1>() = declval<_Up1>())> static true_type __test(int); template<typename, typename> static false_type __test(...); public: typedef decltype(__test<_Tp, _Up>(0)) type; };
相关文章推荐
- JavaScript中的DSL元编程介绍
- ruby元编程实际使用实例
- Ruby和元编程之万物皆为对象
- Ruby元编程之梦中情人method_missing方法详解
- ruby元编程之method_missing的一个使用细节
- ruby元编程之创建自己的动态方法
- Ruby元编程的一些值得注意的地方
- Ruby元编程技术详解(Ruby Metaprogramming techniques)
- 在 Qt4 中使用 C++11
- centos安装devtoolset-3支持gcc 4.9.2
- 使用eclipse编译含有C++11特性的代码
- 怎样在Linux环境编译支持C11
- eclipse支持c++11
- C++11可变参数函数与for循环
- vs2013 编译c++是发现惊天bug
- 简单性能测试函数模板
- 关于C++现状的一些思考
- 用C++11优化矩阵运算的空间和时间效率
- 浅析构造函数之默认构造函数
- c++中返回数组的函数