¿Cómo verificar si el operador == existe?

Intento crear un ejemplo que verifique la existencia del operator== (miembro o función no miembro). Comprobar si una clase tiene un operator== miembro operator== es fácil, pero ¿cómo verificar si tiene un operator== no miembro operator== ?

Esto es lo que tengo que hacer ahora:

 #include  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  struct opEqualExists { struct yes{ char a[1]; }; struct no { char a[2]; }; template  static yes test( typeof(&C::operator==) ); //template  static yes test( ???? ); template  static no test(...); enum { value = (sizeof(test(0)) == sizeof(yes)) }; }; int main() { std::cout<<(int)opEqualExists::value<<std::endl; } 

¿Es posible escribir una función de prueba para probar la existencia de un operator== no miembro operator== ? Si es así, ¿cómo?

Por cierto, he verificado preguntas similares, pero no he encontrado una solución adecuada:
¿Es posible usar SFINAE / templates para verificar si existe un operador?

Esto es lo que intenté:

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

pero la comstackción falla si se elimina el operador no miembro ==

C ++ 03

Siguiendo el truco funciona. Y se puede usar para todos esos operadores:

 namespace CHECK { class No { bool b[2]; }; template No operator== (const T&, const Arg&); bool Check (...); No& Check (const No&); template  struct EqualExists { enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) }; }; } 

Uso:

 CHECK::EqualExists::value; 

La segunda template typename Arg es útil para algunos casos especiales, como A::operator==(short) , donde no es similar a la class sí misma. En tales casos, el uso es:

 CHECK::EqualExists::value // ^^^^^ argument of `operator==` 

Demo


C ++ 11

No necesitamos usar el truco del decltype cuando tenemos decltype

 namespace CHECK { struct No {}; template No operator== (const T&, const Arg&); template struct EqualExists { enum { value = !std::is_same::value }; }; } 

Manifestación

Eche un vistazo a la Biblioteca de verificación de conceptos de Boost (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm .

Le permite escribir requisitos que una clase debe coincidir para que el progtwig compile. Eres relativamente libre con lo que puedes verificar. Por ejemplo, verificar la presencia del operator== de una clase Foo escribiría de la siguiente manera:

 #include  template  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  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)); BOOST_CONCEPT_ASSERT((boost::EqualityComparable)); // need operator!= too } 

Este código comstack bien siempre que una de las dos implementaciones de operator== esté disponible.

Siguiendo los consejos de @Matthieu M. y @Luc Touraille, actualicé el fragmento de código para proporcionar un ejemplo de uso de boost::EqualityComparable . Una vez más, tenga en cuenta que EqualityComparable le obliga a declarar operator!= Too.

También es posible usar solo rasgos de tipo c ++ 11 para verificar la existencia del miembro:

 #include  #include  template struct has_operator_equal_impl { template static auto test(U*) -> decltype(std::declval() == std::declval()); template static auto test(...) -> std::false_type; using type = typename std::is_same(0))>::type; }; template struct has_operator_equal : has_operator_equal_impl::type {}; 

Puedes usar el rasgo así:

 bool test = has_operator_equal::value; 

El tipo resultante de has_operator_equal será std::true_type o std::false_type (porque hereda de un alias de std::is_same::type ), y ambos definen un miembro de value estático que es un booleano.


Si desea poder probar si su clase define operator==(someOtherType) , puede establecer el segundo argumento de plantilla:

 bool test = has_operator_equal::value; 

donde el parámetro de plantilla MyClass sigue siendo la clase que está probando para la presencia de operator== , y long es el tipo con el que desea poder comparar, por ejemplo, para probar que MyClass tiene operator==(long) .

si EqualTo (como en el primer ejemplo) se deja sin especificar, se convertirá en T , dará como resultado la definición normal de operator==(MyClass) .

Nota de precaución : este rasgo en el caso del operator==(long) será verdadero por long , o cualquier valor implícitamente convertible a long , por ejemplo, double , int , etc.


También puede definir verificaciones para otros operadores y funciones, simplemente reemplazando lo que está dentro del tipo de decltype . Para verificar != , Simplemente reemplace

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

con

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

A partir de c ++ 14, las funciones binarias estándar hacen la mayor parte del trabajo para nosotros para la mayoría de los operadores.

 #include  #include  #include  #include  #include  template struct op_valid_impl { template static auto test(int) -> decltype(std::declval()(std::declval(), std::declval()), void(), std::true_type()); template static auto test(...) -> std::false_type; using type = decltype(test(0)); }; template using op_valid = typename op_valid_impl::type; namespace notstd { struct left_shift { template  constexpr auto operator()(L&& l, R&& r) const noexcept(noexcept(std::forward(l) << std::forward(r))) -> decltype(std::forward(l) << std::forward(r)) { return std::forward(l) << std::forward(r); } }; struct right_shift { template  constexpr auto operator()(L&& l, R&& r) const noexcept(noexcept(std::forward(l) >> std::forward(r))) -> decltype(std::forward(l) >> std::forward(r)) { return std::forward(l) >> std::forward(r); } }; } template using has_equality = op_valid>; template using has_inequality = op_valid>; template using has_less_than = op_valid>; template using has_less_equal = op_valid>; template using has_greater_than = op_valid>; template using has_greater_equal = op_valid>; template using has_bit_xor = op_valid>; template using has_bit_or = op_valid>; template using has_left_shift = op_valid; template using has_right_shift = op_valid; int main() { assert(( has_equality() )); assert((not has_equality()())); assert((has_equality()())); assert(( has_inequality() )); assert(( has_less_than() )); assert(( has_greater_than() )); assert(( has_left_shift() )); assert(( has_left_shift() )); assert(( has_left_shift() )); assert((not has_right_shift()())); assert((has_right_shift()())); assert((not has_right_shift()())); } 

Sé que esta pregunta ha sido respondida hace mucho tiempo, pero pensé que valdría la pena señalar para cualquiera que encuentre esta pregunta en el futuro que Boost acaba de agregar un grupo de rasgos “tiene operador” a su biblioteca type_traits, y entre ellos está has_equal_to , que hace lo que OP estaba pidiendo.

Esta pregunta ya ha sido respondida varias veces, pero hay una forma más simple de verificar la existencia del operator== o básicamente cualquier otra operación (por ejemplo, probar una función miembro con un cierto nombre), usando decltype junto con , operador:

 namespace detail { template struct has_operator_equals_impl { template // 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(), std::declval())); }; } // namespace detail template struct has_operator_equals : detail::has_operator_equals_impl::type {}; 

Puede usar este mismo enfoque para verificar si un tipo T tiene una función miembro foo que es invocable con una cierta lista de argumentos:

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

Creo que esto hace que la intención del código sea mucho más clara. Además de eso, esta es una solución C ++ 11, por lo que no depende de ninguna característica más nueva de C ++ 14 o C ++ 17. El resultado final es el mismo, por supuesto, pero este se ha convertido en mi idioma preferido para probar este tipo de cosas.

Editar: solucionado el caso demencial del operador de coma sobrecargado, siempre lo extraño.

Solo como referencia, publico cómo resolví mi problema, sin necesidad de verificar si el operator== existe:

 #include  #include  struct A { int a; char b; #if 0 bool operator==( const A& r ) const { std::cout<<"calling member function"< 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=="< 

OMI, esto debe ser parte de la clase en sí, ya que trata con los atributos privados de la clase. Las plantillas se interpretan en tiempo de comstackción. Por defecto, genera operator== , constructor, destructor y copy constructor que hacen una copia bit-wise (copia superficial) o comparaciones bit-wise para el objeto del mismo tipo. Los casos especiales (diferentes tipos) deben estar sobrecargados. Si usa la función de operador global, deberá declarar la función como amigo para acceder a la parte privada o bien deberá exponer las interfaces requeridas. A veces esto es realmente feo y puede causar una exposición innecesaria de una función.

Consideremos una metafunción de la siguiente forma, que verifica la existencia de un operador de igualdad (es decir, == ) para el tipo dado:

 template struct equality { .... }; 

Sin embargo, eso podría no ser lo suficientemente bueno para algunos casos de esquina. Por ejemplo, supongamos que su clase X define operator== pero no devuelve bool , sino que devuelve Y Entonces, en este caso, ¿qué debería devolver la equality::value ? true o false ? Bueno, eso depende del caso de uso específico que no conocemos ahora, y no parece ser una buena idea asumir algo y forzarlo a los usuarios. Sin embargo, en general podemos suponer que el tipo de retorno debe ser bool , así que permite express esto en la interfaz misma:

 template struct equality { .... }; 

El valor predeterminado para R es bool que indica que es el caso general. En los casos en que el tipo de retorno del operator== es diferente, responda Y , entonces puede decir esto:

 equality //return type = Y 

que también verifica el tipo de retorno dado. Por defecto,

 equality //return type = bool 

Aquí hay una implementación de esta metafunción:

 namespace details { template  struct equality : std::false_type {}; template  struct equality()==std::declval())> : std::true_type {}; } template struct equality : details::equality {}; 

Prueba:

 struct A {}; struct B { bool operator == (B const &); }; struct C { short operator == (C const &); }; int main() { std::cout<< "equality::value = " << equality::value << std::endl; std::cout<< "equality::value = " << equality::value << std::endl; std::cout<< "equality::value = " << equality::value << std::endl; std::cout<< "equality::value = " << equality::value << std::endl; std::cout<< "equality::value = " << equality::value << std::endl; } 

Salida:

 equality::value = 0 equality::value = 1 equality::value = 0 equality::value = 0 equality::value = 1 

Demo en línea

Espero que ayude.

c ++ 17 versión ligeramente modificada de Richard Hodges godbolt

 #include  #include  template std::is_convertible, R> is_invokable_test(int); template std::false_type is_invokable_test(...); template using is_invokable = decltype(is_invokable_test(0)); template constexpr auto is_invokable_v = is_invokable::value; template using has_equality = is_invokable, bool, L, R>; template constexpr auto has_equality_v = has_equality::value; struct L{}; int operator ==(int, L&&); static_assert(has_equality_v); static_assert(!has_equality_v); static_assert(!has_equality_v); static_assert(has_equality_v);