前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++11 元编程(meta-programming)判断T是否有==操作符

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

作者头像
10km
发布2022-05-07 10:11:24
2760
发布2022-05-07 10:11:24
举报
文章被收录于专栏:10km的专栏10km的专栏

前几天看了《C++11之美》受到一些启发,想到可以通过判断一个类型是否有指定的操作符(比如==,>=)。 基本的原理与文中的差不多,利用SFINAE原则,通过返回类型后置来推断表达式的类型,推断的过程中利用declval,它可以获取类型的右值引用,以便来调用==操作符,这个过程是在编译期完成的。 如果通过==操作符比较declval的右值引用成功了,则会继续推断逗号表达式的类型,最终推断的函数返回类型为bool; 如果通过==操作符比较declval的右值引用失败了,则推断失败,编译器会选择优先级最低的test(...)函数,它的返回类型为void。 我们最后判断实例化的test<T>(0)的返回值是否为bool,可以知道类型T是否存在==操作符。

代码语言:javascript
复制
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类型。

基于上面这个元函数的原理,我们还可以继续写出其他操作符的判断函数,比如>,*操作符。 下面是完整的代码

代码语言:javascript
复制
#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的使用场景的例子:

代码语言:javascript
复制
    /* 判断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相关源码:

代码语言:javascript
复制
  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;
    };
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 后记:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档