How to check if operator == exists?

I am trying to create an example that checks the existence of operator== (member function or non-member). Checking if a class is a member of operator== is easy, but how to check if it has a non-member operator== ?

This is what I need:

 #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 ( la==ra); } #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 check for the existence of a non-member operator== ? If so, how?

btw I checked similar questions but couldn't find the right solution:
Is it possible to use SFINAE / templates to check for an operator?

This is what I tried:

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

but compilation fails if the operator == is deleted

+42
c ++ sfinae
Jun 30 '11 at 11:31
source share
8 answers

C ++ 03

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

Using:

 CHECK::EqualExists<A>::value; 

The second template typename Arg is useful for some special cases, such as A::operator==(short) , where it does not look like a class . In such cases, the use of:

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

Demo




C ++ 11

We do not need to use the sizeof trick when we have 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

+32
Jun 30 2018-11-11T00:
source share

Check out the Boost Feature Checker Library (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm .

It allows you to write requirements that a class must match to compile a program. You are relatively free to check. For example, checking for the presence of operator== class Foo will be written as follows:

 #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 as long as one of the two implementations of operator== .

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

+16
Jun 30 '11 at 12:44
source share

It is also possible to use only features like C ++ 11 to verify member existence:

 #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 this trait like this:

 bool test = has_operator_equal<MyClass>::value; 

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




If you want to check if your class defines operator==(someOtherType) , you can set the second argument to the template:

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

where the MyClass template parameter is still the class you are testing for operator== , and long is the type you want to compare with, for example. to verify that MyClass has operator==(long) .

if EqualTo (as in the first example) is left unspecified, the default will be T , the result will be the normal definition operator==(MyClass) .

Please note: this attribute in the case of operator==(long) will be true for long , or any value implicitly converted to long , for example. double , int , etc.




You can also define checks for other operators and functions by simply replacing what is inside decltype . To check != Just replace

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

from

 static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>()); 
+9
Feb 04 '16 at 17:34
source share

I know that this question has been answered for a long time, but I thought it might be interesting if someone finds this question in the future, that Boost just added a bunch of signs "has an operator" to its type_traits library, and among them there are has_equal_to , which does what the OP requested.

+1
Jan 12 2018-12-12T00:
source share

As with C ++ 14, standard functions for most operations are performed for most operators.

 #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&>()())); } 
+1
Sep 06 '16 at 11:46 on
source share

IMO, this should be part of the class itself, as it concerns the private attributes of the class. Patterns are interpreted at compile time. By default, it generates operator== constructors, a constructor, a destructor, and a copy that perform a bitwise copy (shallow copy) or bitwise comparisons for an object of the same type. Special cases (different types) must be overloaded. If you use the global operator function, you will need to declare the function as a friend to access the private part, otherwise you must provide the necessary interfaces. Sometimes it is really ugly, which can cause an unnecessary function of the function.

0
Jun 30 2018-11-11T00:
source share

Just for reference, I am posting how I solved my problem, without having to check if 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==ra ) && ( b==rb ) ); } #endif }; #if 1 bool operator==( const A &l,const A &r ) { std::cout<<"calling NON-member function"<<std::endl; return ( ( la==ra ) &&( lb==rb ) ); } #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( lp, rp, 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; } 
0
Jul 01 '11 at 7:30
source share

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

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

However, this may not be enough for some corner cases. For example, let's say your class X defines operator== , but it does not return bool , instead it returns Y So, in this case, what should equality<X>::value return? true or false ? Well, it depends on the specific use case that we donโ€™t know now, and it seems that it is not recommended to accept anything and impose it on users. However, in general, we can assume that the return type should be bool , so let's express this in the interface itself:

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

The default value for R is bool , which indicates a general case. In cases where the return type operator== is different, for example Y , you can say the following:

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

which also checks for a given return type. 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 this helps.

0
Apr 01 '16 at 15:52
source share



All Articles