C++11 元编程(meta-programming)判断T是否有==操作符

前几天看了《C++11之美》受到一些启发,想到可以通过判断一个类型是否有指定的操作符(比如==,>=)。
基本的原理与文中的差不多,利用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;
    };

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值