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

BЈо*_*вић 50 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是我试过的:

template <typename C> static yes test( const C*,bool(*)(const C&,constC&) = &operator== );
Run Code Online (Sandbox Code Playgroud)

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

iam*_*ind 39

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)) };
  };  
}
Run Code Online (Sandbox Code Playgroud)

用法:

CHECK::EqualExists<A>::value;
Run Code Online (Sandbox Code Playgroud)

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

CHECK::EqualExists<A, short>::value
//                    ^^^^^ argument of `operator==`
Run Code Online (Sandbox Code Playgroud)

演示.


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(std::declval<T>() == std::declval<Arg>)()), No>::value };
  };  
}
Run Code Online (Sandbox Code Playgroud)

演示

  • 毫无疑问,`sizeof(char)`总是1.但是既然你想通过比较返回类型的大小来区分你的`operator ==`和一个返回`bool`的普通的那个,你需要比较`sizeof(bool)`.但是[`sizeof(bool)`是实现定义的](http://stackoverflow.com/questions/4897844/is-sizeofbool-defined),因此你不知道七个字符是否与1个bool相同,或者还要别的吗.你只知道2个bool占用的空间比一个多:`sizeof(bool)!= sizeof(bool [2])`.但是,那是唯一的挑选;) (5认同)
  • 您在 C++11 版本中编写了“&lt;”而不是“==”:-) (2认同)

jop*_*rat 16

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

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

#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
}
Run Code Online (Sandbox Code Playgroud)

只要两个实现中的operator==一个可用,此代码就可以编译.

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

  • 我认为Boost Concept Checking Library已经定义了一组"标准"概念,如[EqualityComparable](http://www.sgi.com/tech/stl/EqualityComparable.html)(参见[参考](http: //www.boost.org/doc/libs/1_46_1/libs/concept_check/reference.htm)).而且,您的概念不仅需要比较运算符,还需要默认构造函数. (3认同)

Nic*_*aus 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 {};
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用特征:

bool test = has_operator_equal<MyClass>::value;
Run Code Online (Sandbox Code Playgroud)

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


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

bool test = has_operator_equal<MyClass, long>::value;
Run Code Online (Sandbox Code Playgroud)

其中模板参数MyClass仍然是你的存在测试的类operator==,并且long是要能够比较,如:测试的类型MyClassoperator==(long).

if EqualTo(就像在第一个例子中那样)是未指定的,它将默认为T,导致正常的定义operator==(MyClass).

谨慎的注意:这个特性在的情况下,operator==(long)将是真正的long,或隐式转换的任何值long,例如double,int等等.


您还可以定义对其他运算符和函数的检查,只需替换其中的内容即可decltype.要检查!=,只需更换

static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());
Run Code Online (Sandbox Code Playgroud)

static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
Run Code Online (Sandbox Code Playgroud)

  • 我会修改它来检查 `!= false_type` 因为 `==` 可能会返回 `bool` 以外的东西,同时仍然具有相同的语义含义。事实上,这包括“false_type”,所以我会考虑使用完全独特的东西。 (2认同)

Ric*_*ges 6

从 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&>()()));
}
Run Code Online (Sandbox Code Playgroud)


cod*_*101 5

C++20

我猜您想检查用户提供的模板类型是否具有相等运算符,如果是这种情况,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;
}
Run Code Online (Sandbox Code Playgroud)

如果您传递任何没有==!=重载的类型,编译器只会出错并显示消息:

EqualityComparable类型不满足的概念


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

还有更多的概念被添加到标准中,例如Incrementable等等。看看这里