How to check whether operator== exists?

Viewed 25099

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?

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

13 Answers

C++20

I guess you want to check if user provide Template type has equality operator or not, if that is the case Concepts are here to help.

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

If you pass any type that does not have == and != overload the compiler just errors out with message:

EqualityComparable concept not satisfied by type


You can also use std::EqualityComparableWith<T, U> concept can check for those overload between two different types.

There are many more concepts that have been added to standards like Incrementable etc.. Have a look here

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:

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:

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.

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

We can use std::equal_to<Type> (or any other overloaded struct members) to make a more generic solution if we want to test binary operators (or other binary functors).

struct No {};

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

Usage:

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

In addition to @coder3101 answer, concepts can help you implement any function existence tests you want to. For example, std::equality_comparable is implemented using 4 simple tests, that check the following scenarios:

For A and B variables, make sure that the following expressions are valid:

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

If any one of them is illegal at compile time, the program won't compile. The implementation of this test (simplified from the standard):

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

As you can see, you can customize this concept and create your own concept the fulfill your conditions. For example, if you want to force only the existence of operator==, you can do something like this:

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.

Related