如何检查操作符=是否存在?

时间:2021-05-23 07:17:45

I am trying to create an example, which would check the existence of the operator== (member or, non-member function). To check whether a class has a member operator== is easy, but how to check whether it has a non-member operator==?

我正在尝试创建一个示例,它将检查运算符==(成员或非成员函数)的存在性。检查一个类是否有成员运算符==很容易,但是如何检查它是否有非成员运算符==?

This is what I have to far :

这就是我要讲的:

#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;
}

Is it possible to write a test function to test the existence of non-member operator==? If yes, how?

是否可以编写一个测试函数来测试非成员操作符==?如果是,如何?

btw I have checked similar questions, but haven't found a proper solution :
Is it possible to use SFINAE/templates to check if an operator exists?

顺便说一下,我也检查过类似的问题,但是还没有找到合适的解决方案:是否可以使用SFINAE/template来检查操作符是否存在?

This is what I tried :

这就是我所尝试的:

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

but the compilation fails if the non-member operator== is removed

但是,如果删除非成员操作符==,则编译失败。

10 个解决方案

#1


33  

C++03

Following trick works. And it can be used for all such operators:

以下技巧的作品。可用于所有此类操作:

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)) };
  };  
}

Usage:

用法:

CHECK::EqualExists<A>::value;

The 2nd template typename Arg is useful for some special cases like A::operator==(short), where it's not similar to class itself. In such cases the usage is:

第二个模板typename Arg对一些特殊的情况很有用,例如::operator==(short),它与类本身不相似。在这种情况下,使用是:

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

Demo.

演示。


C++11

We need not use sizeof trick when we have decltype

当我们有解密时,我们不需要使用sizeof技巧

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

演示

#2


16  

Have a look at Boost's Concept Check Library (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm.

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

It enables you to write requirements that a class must match in order for the program to compile. You're relatively free with what you can check. For example, verifying the presence of operator== of a class Foo would write as follow:

它允许您编写一个类必须匹配的需求,以便程序编译。你可以相对*地检查。例如,验证某个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
}

This code compiles fine as long as one of the two implementations of operator== is available.

只要操作符== =的两个实现之一可用,此代码就可以很好地编译。

Following @Matthieu M. and @Luc Touraille advice, I updated the code snippet to provide an example of boost::EqualityComparable usage. Once again, please note that EqualityComparable forces you to declare operator!= too.

在@Matthieu M.和@Luc Touraille建议之后,我更新了代码片段,以提供boost: equalitycomparusage的示例。再一次,请注意,相等可比的力量迫使您声明操作符!=。

#3


9  

It's also possible to use only c++11 type traits to check the existence of the member:

也可以只使用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 {};

You can use the trait like so:

你可以这样使用这个特质:

bool test = has_operator_equal<MyClass>::value;

The resulting type of has_operator_equal will either be std::true_type or std::false_type (because it inherits from an alias of std::is_same::type), and both define a static value member which is a boolean.

所产生的has_operator_equal类型将是std::true_type或std::伪类型(因为它继承了std的别名::is_same::type),并且都定义了一个静态值成员,这是一个布尔值。


If you want to be able to test whether your class defines operator==(someOtherType), you can set the second template argument:

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

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

where the template parameter MyClass is still the class that you are testing for the presence of operator==, and long is the type you want to be able to compare to, e.g. to test that MyClass has operator==(long).

如果模板参数MyClass仍然是您要测试的class,以确定是否存在operator== =,而long是您希望能够进行比较的类型,例如,要测试MyClass是否具有operator==(long)。

if EqualTo (like it was in the first example) is left unspecified, it will default to T, result in the normal definition of operator==(MyClass).

如果EqualTo(如第一个例子中所示)未指定,它将默认为T,结果是操作符== =(MyClass)的常规定义。

Note of caution: This trait in the case of operator==(long) will be true for long, or any value implicitly convertible to long, e.g. double, int, etc.

注意:在操作符==(long)的情况下,这个特征将长期成立,或者任何隐式转换为long的值,例如double, int等。


You can also define checks for other operators and functions, just by replacing what's inside the decltype. To check for !=, simply replace

您也可以定义其他操作符和函数的检查,仅仅是替换了解密中的内容。要检查!=,只需替换

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

with

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

#4


2  

As of c++14, the standard binary functions do most of the work for us for the majority of operators.

在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&>()()));
}

#5


1  

I know this question has long since been answered but I thought it might be worth noting for anyone who finds this question in the future that Boost just added a bunch of "has operator" traits to their type_traits library, and among them is has_equal_to, which does what OP was asking for.

我知道这个问题早已被回答,但我认为这可能是值得注意的人发现这个问题在未来增加加了一堆“操作符”特征type_traits图书馆,和其中has_equal_to,OP是什么要求。

#6


1  

This question has already been answered several times, but there is a simpler way to check for the existence of operator== or basically any other operation (e.g., testing for a member function with a certain name), by using decltype together with the , 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 {};

You can use this same approach to check if a type T has a member function foo which is invocable with a certain argument list:

您可以使用相同的方法来检查类型T是否具有成员函数foo,该函数可以用特定的参数列表调用:

namespace detail
{
    template<typename T, typename ...Args>
    struct has_member_foo_impl
    {
        template<typename T_ = T>
        static auto test(T_ &&t, Args &&...args) -> decltype(t.foo(std::forward<Args>(args)...), void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<T>(), std::declval<Args>()...));
    };
} // namespace detail

template<typename T, typename ...Args>
struct has_member_foo : detail::has_member_foo_impl<T, Args...>::type {};

I think this makes the intent of the code much clearer. In addition to that, this is a C++11 solution, so it doesn't depend on any newer C++14 or C++17 features. The end result is the same, of course, but this has become my preferred idiom for testing these kinds of things.

我认为这使代码的意图更加清晰。此外,这是一个c++ 11解决方案,因此它不依赖于任何新的c++ 14或c++ 17特性。当然,最终结果是一样的,但这已经成为我测试这类东西的首选习惯用法。

Edit: Fixed the insane case of the overloaded comma operator, I always miss that.

修正了重载逗号运算符的疯狂情况,我总是忽略它。

#7


0  

Just for a reference, I am posting how I solved my problem, without a need to check if the operator== exists :

仅供参考,我将发布如何解决我的问题,不需要检查操作员==是否存在:

#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, this must be part of the class itself as it's deals with the private attributes of the class. The templates are interpreted at compile time. By default it generates operator==,constructor, destructor and copy constructor which do bit-wise copy (shallow copy) or bit-wise comparisons for the object of same type. The special cases (different types) must be overloaded. If you use global operator function you will have to declare the function as friend to access the private part or else you've to expose the interfaces required. Sometimes this is really ugly which may cause an unnecessary expose of a function.

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

#9


0  

Lets consider a meta-function of the following form, which checks for the existence of equality operator (i.e ==) for the given type:

让我们考虑以下形式的元函数,它检查等式运算符(i)的存在性。(e ==)为给定类型:

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

However, that might not be good enough for some corner cases. For example, say your class X does define operator== but it doesn't return bool, instead it returns Y. So in this case, what should equality<X>::value return? true or false? Well, that depends on the specific use case which we dont know now, and it doesn't seem to be a good idea to assume anything and force it on the users. However, in general we can assume that the return type should be bool, so lets express this in the interface itself:

然而,对于某些特殊情况来说,这可能还不够好。例如,假设你的类X确实定义了操作符==,但是它没有返回bool,而是返回y。那么在这种情况下,等号应该是什么?真或假?这取决于具体的用例,我们现在还不知道,假设任何事情并强加给用户似乎不是个好主意。但是,一般情况下,我们可以假设返回类型应该是bool,所以让我们在接口本身中表示它:

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

The default value for R is bool which indicates it is the general case. In cases, where the return type of operator== is different, say Y, then you can say this:

R的默认值是bool,表示它是一般情况。在某些情况下,如果运算符==的返回类型不同,比如Y,那么可以这样说:

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

which checks for the given return-type as well. By default,

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

equality<X>   //return type = bool

Here is one implementation of this meta-function:

这个元功能的实现如下:

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> {};

Test:

测试:

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;
}

Output:

输出:

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

Online Demo

在线演示

Hope that helps.

希望有帮助。

#10


0  

c++17 slightly modified version of Richard Hodges godbolt

c++17稍微修改过的Richard Hodges godbolt版本

#include <functional>
#include <type_traits>

template<class T, class R, class ... Args>
std::is_convertible<std::invoke_result_t<T, Args...>, R> is_invokable_test(int);

template<class T, class R, class ... Args>
std::false_type is_invokable_test(...);

template<class T, class R, class ... Args>
using is_invokable = decltype(is_invokable_test<T, R, Args...>(0));

template<class T, class R, class ... Args>
constexpr auto is_invokable_v = is_invokable<T, R, Args...>::value;

template<class L, class R = L>
using has_equality = is_invokable<std::equal_to<>, bool, L, R>;
template<class L, class R = L>
constexpr auto has_equality_v = has_equality<L, R>::value;

struct L{};

int operator ==(int, L&&);

static_assert(has_equality_v<int>);
static_assert(!has_equality_v<L>);
static_assert(!has_equality_v<L, int>);
static_assert(has_equality_v<int, L>);

#1


33  

C++03

Following trick works. And it can be used for all such operators:

以下技巧的作品。可用于所有此类操作:

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)) };
  };  
}

Usage:

用法:

CHECK::EqualExists<A>::value;

The 2nd template typename Arg is useful for some special cases like A::operator==(short), where it's not similar to class itself. In such cases the usage is:

第二个模板typename Arg对一些特殊的情况很有用,例如::operator==(short),它与类本身不相似。在这种情况下,使用是:

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

Demo.

演示。


C++11

We need not use sizeof trick when we have decltype

当我们有解密时,我们不需要使用sizeof技巧

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

演示

#2


16  

Have a look at Boost's Concept Check Library (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm.

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

It enables you to write requirements that a class must match in order for the program to compile. You're relatively free with what you can check. For example, verifying the presence of operator== of a class Foo would write as follow:

它允许您编写一个类必须匹配的需求,以便程序编译。你可以相对*地检查。例如,验证某个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
}

This code compiles fine as long as one of the two implementations of operator== is available.

只要操作符== =的两个实现之一可用,此代码就可以很好地编译。

Following @Matthieu M. and @Luc Touraille advice, I updated the code snippet to provide an example of boost::EqualityComparable usage. Once again, please note that EqualityComparable forces you to declare operator!= too.

在@Matthieu M.和@Luc Touraille建议之后,我更新了代码片段,以提供boost: equalitycomparusage的示例。再一次,请注意,相等可比的力量迫使您声明操作符!=。

#3


9  

It's also possible to use only c++11 type traits to check the existence of the member:

也可以只使用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 {};

You can use the trait like so:

你可以这样使用这个特质:

bool test = has_operator_equal<MyClass>::value;

The resulting type of has_operator_equal will either be std::true_type or std::false_type (because it inherits from an alias of std::is_same::type), and both define a static value member which is a boolean.

所产生的has_operator_equal类型将是std::true_type或std::伪类型(因为它继承了std的别名::is_same::type),并且都定义了一个静态值成员,这是一个布尔值。


If you want to be able to test whether your class defines operator==(someOtherType), you can set the second template argument:

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

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

where the template parameter MyClass is still the class that you are testing for the presence of operator==, and long is the type you want to be able to compare to, e.g. to test that MyClass has operator==(long).

如果模板参数MyClass仍然是您要测试的class,以确定是否存在operator== =,而long是您希望能够进行比较的类型,例如,要测试MyClass是否具有operator==(long)。

if EqualTo (like it was in the first example) is left unspecified, it will default to T, result in the normal definition of operator==(MyClass).

如果EqualTo(如第一个例子中所示)未指定,它将默认为T,结果是操作符== =(MyClass)的常规定义。

Note of caution: This trait in the case of operator==(long) will be true for long, or any value implicitly convertible to long, e.g. double, int, etc.

注意:在操作符==(long)的情况下,这个特征将长期成立,或者任何隐式转换为long的值,例如double, int等。


You can also define checks for other operators and functions, just by replacing what's inside the decltype. To check for !=, simply replace

您也可以定义其他操作符和函数的检查,仅仅是替换了解密中的内容。要检查!=,只需替换

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

with

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

#4


2  

As of c++14, the standard binary functions do most of the work for us for the majority of operators.

在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&>()()));
}

#5


1  

I know this question has long since been answered but I thought it might be worth noting for anyone who finds this question in the future that Boost just added a bunch of "has operator" traits to their type_traits library, and among them is has_equal_to, which does what OP was asking for.

我知道这个问题早已被回答,但我认为这可能是值得注意的人发现这个问题在未来增加加了一堆“操作符”特征type_traits图书馆,和其中has_equal_to,OP是什么要求。

#6


1  

This question has already been answered several times, but there is a simpler way to check for the existence of operator== or basically any other operation (e.g., testing for a member function with a certain name), by using decltype together with the , 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 {};

You can use this same approach to check if a type T has a member function foo which is invocable with a certain argument list:

您可以使用相同的方法来检查类型T是否具有成员函数foo,该函数可以用特定的参数列表调用:

namespace detail
{
    template<typename T, typename ...Args>
    struct has_member_foo_impl
    {
        template<typename T_ = T>
        static auto test(T_ &&t, Args &&...args) -> decltype(t.foo(std::forward<Args>(args)...), void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<T>(), std::declval<Args>()...));
    };
} // namespace detail

template<typename T, typename ...Args>
struct has_member_foo : detail::has_member_foo_impl<T, Args...>::type {};

I think this makes the intent of the code much clearer. In addition to that, this is a C++11 solution, so it doesn't depend on any newer C++14 or C++17 features. The end result is the same, of course, but this has become my preferred idiom for testing these kinds of things.

我认为这使代码的意图更加清晰。此外,这是一个c++ 11解决方案,因此它不依赖于任何新的c++ 14或c++ 17特性。当然,最终结果是一样的,但这已经成为我测试这类东西的首选习惯用法。

Edit: Fixed the insane case of the overloaded comma operator, I always miss that.

修正了重载逗号运算符的疯狂情况,我总是忽略它。

#7


0  

Just for a reference, I am posting how I solved my problem, without a need to check if the operator== exists :

仅供参考,我将发布如何解决我的问题,不需要检查操作员==是否存在:

#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, this must be part of the class itself as it's deals with the private attributes of the class. The templates are interpreted at compile time. By default it generates operator==,constructor, destructor and copy constructor which do bit-wise copy (shallow copy) or bit-wise comparisons for the object of same type. The special cases (different types) must be overloaded. If you use global operator function you will have to declare the function as friend to access the private part or else you've to expose the interfaces required. Sometimes this is really ugly which may cause an unnecessary expose of a function.

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

#9


0  

Lets consider a meta-function of the following form, which checks for the existence of equality operator (i.e ==) for the given type:

让我们考虑以下形式的元函数,它检查等式运算符(i)的存在性。(e ==)为给定类型:

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

However, that might not be good enough for some corner cases. For example, say your class X does define operator== but it doesn't return bool, instead it returns Y. So in this case, what should equality<X>::value return? true or false? Well, that depends on the specific use case which we dont know now, and it doesn't seem to be a good idea to assume anything and force it on the users. However, in general we can assume that the return type should be bool, so lets express this in the interface itself:

然而,对于某些特殊情况来说,这可能还不够好。例如,假设你的类X确实定义了操作符==,但是它没有返回bool,而是返回y。那么在这种情况下,等号应该是什么?真或假?这取决于具体的用例,我们现在还不知道,假设任何事情并强加给用户似乎不是个好主意。但是,一般情况下,我们可以假设返回类型应该是bool,所以让我们在接口本身中表示它:

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

The default value for R is bool which indicates it is the general case. In cases, where the return type of operator== is different, say Y, then you can say this:

R的默认值是bool,表示它是一般情况。在某些情况下,如果运算符==的返回类型不同,比如Y,那么可以这样说:

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

which checks for the given return-type as well. By default,

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

equality<X>   //return type = bool

Here is one implementation of this meta-function:

这个元功能的实现如下:

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> {};

Test:

测试:

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;
}

Output:

输出:

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

Online Demo

在线演示

Hope that helps.

希望有帮助。

#10


0  

c++17 slightly modified version of Richard Hodges godbolt

c++17稍微修改过的Richard Hodges godbolt版本

#include <functional>
#include <type_traits>

template<class T, class R, class ... Args>
std::is_convertible<std::invoke_result_t<T, Args...>, R> is_invokable_test(int);

template<class T, class R, class ... Args>
std::false_type is_invokable_test(...);

template<class T, class R, class ... Args>
using is_invokable = decltype(is_invokable_test<T, R, Args...>(0));

template<class T, class R, class ... Args>
constexpr auto is_invokable_v = is_invokable<T, R, Args...>::value;

template<class L, class R = L>
using has_equality = is_invokable<std::equal_to<>, bool, L, R>;
template<class L, class R = L>
constexpr auto has_equality_v = has_equality<L, R>::value;

struct L{};

int operator ==(int, L&&);

static_assert(has_equality_v<int>);
static_assert(!has_equality_v<L>);
static_assert(!has_equality_v<L, int>);
static_assert(has_equality_v<int, L>);