如何检查operator ==是否存在?

时间:2011-06-30 11:31:17

标签: c++ sfinae

我正在尝试创建一个示例,它将检查operator==(成员或非成员函数)的存在。要检查某个班级是否有成员operator==很容易,但如何检查其是否有非成员operator==

这就是我所要做的:

#include <iostream>

struct A
{
    int  a;

    #if 0
    bool operator==( const A& rhs ) const
    {
        return ( a==rhs.a);
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    return ( l.a==r.a);
}
#endif


template < typename T >
struct opEqualExists
{
    struct yes{ char a[1]; };
    struct no { char a[2]; };

    template <typename C> static yes test( typeof(&C::operator==) );
    //template <typename C> static yes test( ???? );
    template <typename C> static no test(...);

    enum { value = (sizeof(test<T>(0)) == sizeof(yes)) };
};

int main()
{
    std::cout<<(int)opEqualExists<A>::value<<std::endl;
}

是否可以编写测试函数来测试非成员operator==的存在? 如果是,怎么样?

是的,我检查了类似的问题,但没有找到合适的解决方案:
Is it possible to use SFINAE/templates to check if an operator exists?

这就是我的尝试:

template <typename C> static yes test( const C*,bool(*)(const C&,constC&) = &operator== );

但如果非成员运算符==被删除,则编译失败

12 个答案:

答案 0 :(得分:37)

C ++ 03

以下技巧有效。它可以用于所有这些运营商:

namespace CHECK
{
  class No { bool b[2]; };
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  bool Check (...);
  No& Check (const No&);

  template <typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) };
  };  
}

用法:

CHECK::EqualExists<A>::value;

第二个template typename Arg对某些特殊情况很有用,例如A::operator==(short),它与class本身不相似。在这种情况下,用法是:

CHECK::EqualExists<A, short>::value
//                    ^^^^^ argument of `operator==`

Demo


C ++ 11

当我们有sizeof

时,我们无需使用decltype技巧
namespace CHECK
{
  struct No {}; 
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  template<typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = !std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value };
  };  
}

Demo

答案 1 :(得分:16)

查看 Boost的概念检查库(BCCL)http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm

它使您能够编写类必须匹配的要求才能编译程序。你可以检查的是相对自由的。例如,验证类Foo的operator==的存在将写如下:

#include <boost/concept_check.hpp>


template <class T>
struct opEqualExists;

class Foo {
public:
    bool operator==(const Foo& f) {
       return true;
    }

   bool operator!=(const Foo& f) {
      return !(*this == f);
   }

   // friend bool operator==(const Foo&, const Foo&);
   // friend bool operator!=(const Foo&, const Foo&);
};

template <class T>
struct opEqualExists {
   T a;
   T b;

   // concept requirements  
   BOOST_CONCEPT_USAGE(opEqualExists) {
      a == b;
   }
};


/*
bool operator==(const Foo& a, const Foo& b) {
   return true; // or whatever
}
*/


/*
bool operator!=(const Foo& a, const Foo& b) {
   return ! (a == b); // or whatever
}
*/


int main() {
   // no need to declare foo for interface to be checked

   // declare that class Foo models the opEqualExists concept
   //   BOOST_CONCEPT_ASSERT((opEqualExists<Foo>));
   BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Foo>)); // need operator!= too
}

只要operator==的两个实现之一可用,此代码就可以正常编译。

按照@Matthieu M.和@Luc Touraille的建议,我更新了代码段,以提供boost::EqualityComparable用法的示例。请再次注意,EqualityComparable会强制您声明operator!=

答案 2 :(得分:10)

也可以只使用c ++ 11类型特征来检查成员是否存在:

#include <type_traits>
#include <utility>

template<class T, class EqualTo>
struct has_operator_equal_impl
{
    template<class U, class V>
    static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());
    template<typename, typename>
    static auto test(...) -> std::false_type;

    using type = typename std::is_same<bool, decltype(test<T, EqualTo>(0))>::type;
};

template<class T, class EqualTo = T>
struct has_operator_equal : has_operator_equal_impl<T, EqualTo>::type {};

你可以像这样使用这个特性:

bool test = has_operator_equal<MyClass>::value;

结果类型的has_operator_equal将是std::true_type or std::false_type(因为它继承自std::is_same::type的别名),并且都定义了一个静态value成员,它是一个布尔值

如果您希望能够测试您的类是否定义operator==(someOtherType),您可以设置第二个模板参数:

bool test = has_operator_equal<MyClass, long>::value;

其中模板参数MyClass仍然是您正在测试operator==是否存在的类,long是您希望能够与之比较的类型,例如测试MyClass是否有operator==(long)

如果EqualTo(就像在第一个示例中那样)未指定,则默认为T,导致operator==(MyClass)的正常定义。

注意事项:对于operator==(long)long任何可隐式转换为的值都属于long,此特征属于double 1}},例如intdecltype

您还可以定义对其他运算符和函数的检查,只需替换!=中的内容即可。要检查static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>()); ,只需替换

即可
static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());

$('#iframe1').contents().find('#iframe2').contents().find('#btn1')

答案 3 :(得分:3)

从c ++ 14开始,标准二进制函数为大多数运算符完成了大部分工作。

#include <utility>
#include <iostream>
#include <string>
#include <algorithm>
#include <cassert>


template<class X, class Y, class Op>
struct op_valid_impl
{
    template<class U, class L, class R>
    static auto test(int) -> decltype(std::declval<U>()(std::declval<L>(), std::declval<R>()),
                                      void(), std::true_type());

    template<class U, class L, class R>
    static auto test(...) -> std::false_type;

    using type = decltype(test<Op, X, Y>(0));

};

template<class X, class Y, class Op> using op_valid = typename op_valid_impl<X, Y, Op>::type;

namespace notstd {

    struct left_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) << std::forward<R>(r)))
        -> decltype(std::forward<L>(l) << std::forward<R>(r))
        {
            return std::forward<L>(l) << std::forward<R>(r);
        }
    };

    struct right_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) >> std::forward<R>(r)))
        -> decltype(std::forward<L>(l) >> std::forward<R>(r))
        {
            return std::forward<L>(l) >> std::forward<R>(r);
        }
    };

}

template<class X, class Y> using has_equality = op_valid<X, Y, std::equal_to<>>;
template<class X, class Y> using has_inequality = op_valid<X, Y, std::not_equal_to<>>;
template<class X, class Y> using has_less_than = op_valid<X, Y, std::less<>>;
template<class X, class Y> using has_less_equal = op_valid<X, Y, std::less_equal<>>;
template<class X, class Y> using has_greater_than = op_valid<X, Y, std::greater<>>;
template<class X, class Y> using has_greater_equal = op_valid<X, Y, std::greater_equal<>>;
template<class X, class Y> using has_bit_xor = op_valid<X, Y, std::bit_xor<>>;
template<class X, class Y> using has_bit_or = op_valid<X, Y, std::bit_or<>>;
template<class X, class Y> using has_left_shift = op_valid<X, Y, notstd::left_shift>;
template<class X, class Y> using has_right_shift = op_valid<X, Y, notstd::right_shift>;

int main()
{
    assert(( has_equality<int, int>() ));
    assert((not has_equality<std::string&, int const&>()()));
    assert((has_equality<std::string&, std::string const&>()()));
    assert(( has_inequality<int, int>() ));
    assert(( has_less_than<int, int>() ));
    assert(( has_greater_than<int, int>() ));
    assert(( has_left_shift<std::ostream&, int>() ));
    assert(( has_left_shift<std::ostream&, int&>() ));
    assert(( has_left_shift<std::ostream&, int const&>() ));

    assert((not has_right_shift<std::istream&, int>()()));
    assert((has_right_shift<std::istream&, int&>()()));
    assert((not has_right_shift<std::istream&, int const&>()()));
}

答案 4 :(得分:2)

这个问题已经多次得到解答,但有一种更简单的方法可以检查是否存在 ocamlfind list | grep llvm 或基本上任何其他操作(例如,测试具有特定名称的成员函数),使用operator==以及decltype运营商:

,

您可以使用相同的方法来检查类型namespace detail { template<typename L, typename R> struct has_operator_equals_impl { template<typename T = L, typename U = R> // template parameters here to enable SFINAE static auto test(T &&t, U &&u) -> decltype(t == u, void(), std::true_type{}); static auto test(...) -> std::false_type; using type = decltype(test(std::declval<L>(), std::declval<R>())); }; } // namespace detail template<typename L, typename R = L> struct has_operator_equals : detail::has_operator_equals_impl<L, R>::type {}; 是否具有可以使用特定参数列表调用的成员函数T

foo

我认为这使得代码的意图更加清晰。除此之外,这是一个C ++ 11解决方案,因此它不依赖于任何较新的C ++ 14或C ++ 17功能。当然,最终的结果是一样的,但这已成为我测试这类事物的首选习惯。

编辑:修复了重载逗号运算符的疯狂情况,我总是想念它。

答案 5 :(得分:1)

C ++ 20

我想您想检查用户提供的Template类型是否具有相等运算符,如果是这种情况,这里的Concepts可以为您提供帮助。

#include<concepts>

struct S{
   int x;
};

template<class T>
requires std::EqualityComparable<T>
void do_magic(T a, T b){
    return a == b;
}

int main(){
    // do_magic(S{}, S{}); Compile time error
    do_magic(56, 46); // Okay int has == and !=

return 0;
}

如果您传递的任何类型都没有==!=重载,则编译器会错误提示:

  

EqualityComparable概念不满足类型


您还可以使用std::EqualityComparableWith<T, U>概念来检查两种不同类型之间的重载。

Incrementable等标准中已经添加了更多概念。请看here

答案 6 :(得分:1)

我知道这个问题早已得到解答,但我认为,对于今后发现这个问题的人来说,Boost只是在他们的type_traits库中添加了一堆“has operator”特征,其中包括has_equal_to,这正是OP所要求的。

答案 7 :(得分:0)

仅供参考,我发布了解决问题的方法,无需检查operator==是否存在:

#include <iostream>
#include <cstring>

struct A
{
    int  a;
    char b;

    #if 0
    bool operator==( const A& r ) const
    {
        std::cout<<"calling member function"<<std::endl;

        return ( ( a==r.a ) && ( b==r.b ) );
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    std::cout<<"calling NON-member function"<<std::endl;
    return ( ( l.a==r.a ) &&( l.b==r.b ) );
}
#endif

namespace details
{
struct anyType
{
    template < class S >
    anyType( const S &s ) :
        p(&s),
        sz(sizeof(s))
    {
    }

    const void *p;
    int sz;
};
bool operator==( const anyType &l, const anyType &r )
{
    std::cout<<"anyType::operator=="<<std::endl;
    return ( 0 == std::memcmp( l.p, r.p, l.sz ) );
}
} // namespace details

int main()
{
    A a1;
    a1.a=3;a1.b=0x12;
    A a2;
    a2.a=3;a2.b=0x12;

    using details::operator==;

    std::cout<< std::boolalpha << "numbers are equals : " << ( a1 == a2 ) <<std::endl;
}

答案 8 :(得分:0)

IMO,这必须是类本身的一部分,因为它处理类的私有属性。模板在编译时解释。默认情况下,它生成operator==,构造函数,析构函数和复制构造函数,它们对相同类型的对象进行逐位复制(浅复制)或逐位比较。特殊情况(不同类型)必须重载。如果使用全局运算符函数,则必须将该函数声明为访问私有部分的朋友,否则您将公开所需的接口。有时候这很丑陋,可能导致不必要的函数暴露。

答案 9 :(得分:0)

让我们考虑以下形式的元函数,它检查给定类型是否存在等于运算符(即==):

template<typename T>
struct equality { .... };

然而,对于某些角落案例而言,这可能不够好。例如,假设您的班级X确实定义了operator==,但它不会返回bool,而是返回Y。那么在这种情况下,equality<X>::value应该返回什么? truefalse?嗯,这取决于我们现在不知道的具体用例,并且假设任何东西并强制它在用户身上似乎不是一个好主意。但是,通常我们可以假设返回类型应该是bool,所以让我们在界面本身表达:

template<typename T, typename R = bool>
struct equality { .... };

R的默认值为bool,表示这是一般情况。如果operator==的返回类型不同,请说Y,那么您可以这样说:

equality<X, Y>  //return type = Y

也会检查给定的返回类型。默认情况下,

equality<X>   //return type = bool

以下是此元函数的一个实现:

namespace details
{
    template <typename T, typename R, typename = R>
    struct equality : std::false_type {};

    template <typename T, typename R>
    struct equality<T,R,decltype(std::declval<T>()==std::declval<T>())> 
       : std::true_type {};
}

template<typename T, typename R = bool>
struct equality : details::equality<T, R> {};

测试:

struct A  {};
struct B  {  bool operator == (B const &); };
struct C  {  short operator == (C const &); };

int main()
{
    std::cout<< "equality<A>::value = " << equality<A>::value << std::endl;
    std::cout<< "equality<B>::value = " << equality<B>::value << std::endl;
    std::cout<< "equality<C>::value = " << equality<C>::value << std::endl;
    std::cout<< "equality<B,short>::value = " << equality<B,short>::value << std::endl;
    std::cout<< "equality<C,short>::value = " << equality<C,short>::value << std::endl;
}

输出:

equality<A>::value = 0
equality<B>::value = 1
equality<C>::value = 0
equality<B,short>::value = 0
equality<C,short>::value = 1

Online Demo

希望有所帮助。

答案 10 :(得分:0)

除了@coder3101 answerconcepts还可以帮助您实现所需的任何功能存在测试。例如,std::equality_comparable是使用4个简单的测试实现的,这些测试检查以下情况:

对于AB变量,请确保以下表达式有效:

A == B, returns bool
A != B, returns bool
B == A, returns bool
B != A, returns bool

如果其中任何一个在编译时是非法的,该程序将不会编译。此测试的实现(从标准简化):

template <typename T> concept equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { t != u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
        { u != t } -> std::convertible_to<bool>;
    };

如您所见,您可以自定义此概念并创建自己的概念来满足您的条件。例如,如果您只想强制operator==存在,则可以执行以下操作:

template <typename T> concept my_equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
    };

Read more about concepts in C++20.

答案 11 :(得分:0)

我们可以使用std::equal_to<Type>(或任何其他重载的struct成员)提出更通用的解决方案。

struct No {};

template<class T, class Operator>
struct ExistsOperator
{
    enum { value = !std::is_same<decltype(std::declval<Operator>()(std::declval<T>(), std::declval<T>())), No>::value };
};

用法:

using Type = int;
constexpr bool hasEqual = ExistsOperator<Type, std::equal_to<Type>>::value;