58

我正在尝试创建一个示例,它将检查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;
}

是否可以编写一个测试函数来测试 non-member 的存在operator==?如果是,如何?

顺便说一句,我检查了类似的问题,但还没有找到合适的解决方案:
Is it possible to use SFINAE/templates to check if an operator是否存在?

这是我尝试过的:

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

但是如果非成员 operator== 被删除,编译会失败

4

13 回答 13

47

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

用法:

CHECK::EqualExists<A>::value;

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

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

演示


C++11

当我们有和时,我们不需要使用sizeof和空引用技巧decltypestd::declval

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

演示

于 2011-06-30T14:17:11.570 回答
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
}

只要 的两个实现之一operator==可用,此代码就可以正常编译。

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

于 2011-06-30T12:44:45.503 回答
12

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

您可以像这样使用特征:

bool test = has_operator_equal<MyClass>::value;

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


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

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

其中模板参数MyClass仍然是您正在测试是否存在的类operator==,并且long是您希望能够与之比较的类型,例如测试MyClass具有operator==(long).

如果EqualTo(就像在第一个示例中一样)未指定,它将默认为T,导致operator==(MyClass).

注意事项:在 的情况下,此特征对于或任何可隐式转换为的值operator==(long)都为真,例如,等。longlongdoubleint


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

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

static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
于 2016-02-04T17:34:39.747 回答
8

从 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&>()()));
}
于 2016-09-06T11:46:13.263 回答
6

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

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

EqualityComparable类型不满足的概念


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

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

于 2019-08-04T17:44:06.633 回答
3

我知道这个问题早已得到解答,但我认为对于将来发现这个问题的任何人来说,Boost 刚刚在他们的 type_traits 库中添加了一堆“has operator”特征,其中包括has_equal_to,这可能是值得注意的做OP所要求的。

于 2012-01-12T02:58:14.283 回答
3

这个问题已经回答了好几次了,但是有一个更简单的方法来检查是否存在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 {};

您可以使用相同的方法来检查类型是否具有可通过特定参数列表调用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 {};

我认为这使代码的意图更加清晰。除此之外,这是一个 C++11 解决方案,因此它不依赖于任何较新的 C++14 或 C++17 功能。当然,最终结果是相同的,但这已成为我测试这类事物的首选习惯用法。

编辑:修复了重载逗号运算符的疯狂情况,我总是想念它。

于 2018-05-31T19:54:59.810 回答
1

==让我们考虑以下形式的元函数,它检查给定类型是否存在相等运算符(即):

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

但是,对于某些极端情况,这可能还不够好。例如,假设您的类X确实定义operator==但它没有返回bool,而是返回Y。那么在这种情况下,应该equality<X>::value返回什么? true还是false?嗯,这取决于我们现在不知道的具体用例,假设任何事情并将其强加给用户似乎不是一个好主意。但是,通常我们可以假设返回类型应该是bool,所以让我们在接口本身中表达它:

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

的默认值Rbool表示它是一般情况。在返回类型operator==不同的情况下,比如说Y,那么你可以这样说:

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

它也会检查给定的返回类型。默认,

equality<X>   //return type = bool

这是此元功能的一种实现:

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

测试:

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

输出:

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

在线演示

希望有帮助。

于 2016-04-01T15:52:14.377 回答
1

std::equal_to<Type>如果我们想测试二元运算符(或其他二元仿函数),我们可以使用(或任何其他重载的结构成员)来制定更通用的解决方案。

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

用法:

using Type = int;
constexpr bool hasEqual = ExistsBinaryOperator<Type, std::equal_to<Type>>::value;
于 2020-08-21T10:20:38.613 回答
1

c++17微改版 Richard Hodges神螺栓

#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>);
于 2018-02-27T08:47:31.117 回答
0

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

于 2011-06-30T13:47:27.157 回答
0

仅供参考,我发布了我如何解决我的问题,而无需检查是否operator==存在:

#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;
}
于 2011-07-01T07:30:41.840 回答
0

除了@coder3101 answer之外,concepts还可以帮助您实现任何您想要的功能存在测试。例如,std::equality_comparable使用 4 个简单的测试来实现,这些测试检查以下场景:

对于AB变量,请确保以下表达式有效:

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

如果其中任何一个在编译时是非法的,程序将无法编译。此测试的实现(从标准简化):

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

如您所见,您可以自定义此概念并创建自己的概念以满足您的条件。例如,如果您只想强制 的存在operator==,您可以执行以下操作:

template <typename T> concept my_equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
    };

阅读更多关于conceptsC++20 的内容。

于 2020-07-10T15:52:31.650 回答