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

发布于 2024-11-17 21:38:27 字数 1357 浏览 4 评论 0 原文

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

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

顺便说一句,我检查过类似的问题,但没有找到合适的解决方案:
是否可以使用 SFINAE /templates 检查运算符是否存在?

这是我尝试过的:

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

睫毛上残留的泪 2024-11-24 21:38:27

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;

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

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

演示


C++11

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

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

演示

C++03

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

Usage:

CHECK::EqualExists<A>::value;

The 2nd template typename Arg is useful for some special cases like A::operator==(short), where it's not similar to class itself. In such cases the usage is:

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

Demo.


C++11

We need not use sizeof and null reference trick when we have decltype and std::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 };
  };  
}

Demo

游魂 2024-11-24 21:38:27

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

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

#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!=

Have a look at Boost's Concept Check Library (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm.

It enables you to write requirements that a class must match in order for the program to compile. You're relatively free with what you can check. For example, verifying the presence of operator== of a class Foo would write as follow:

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

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

ㄟ。诗瑗 2024-11-24 21:38:27

也可以仅使用 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_typestd::false_type(因为它继承自 std::is_same::type 的别名),并且都定义了一个静态 成员,它是一个布尔值。


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

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

其中模板参数 MyClass 仍然是类您正在测试 operator== 是否存在,而 long 是您希望能够进行比较的类型,例如测试 MyClassoperator==(long)

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

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


您还可以定义对其他运算符和函数的检查,只需替换decltype 里面有什么。要检查 !=,只需替换

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

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

It's also possible to use only c++11 type traits to check the existence of the member:

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

bool test = has_operator_equal<MyClass>::value;

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


If you want to be able to test whether your class defines operator==(someOtherType), you can set the second template argument:

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

where the template parameter MyClass is still the class that you are testing for the presence of operator==, and long is the type you want to be able to compare to, e.g. to test that MyClass has operator==(long).

if EqualTo (like it was in the first example) is left unspecified, it will default to T, result in the normal definition of operator==(MyClass).

Note of caution: This trait in the case of operator==(long) will be true for long, or any value implicitly convertible to long, e.g. double, int, etc.


You can also define checks for other operators and functions, just by replacing what's inside the decltype. To check for !=, simply replace

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

with

static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
空宴 2024-11-24 21:38:27

C++20

我猜你想检查用户提供的类型是否有相等运算符;如果是这种情况,Concepts 将为您提供帮助。

#include <concepts>

struct S{
   int x;
};

template<std::equality_comparable T>
bool 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 !=
}

如果您传递任何未定义 ==!= 的类型,编译器只会出错并显示消息,例如:

equality_comparable 概念不满足 type


您还可以使用 std::equality_comparable_with 概念来检查两种不同类型之间的重载。

标准中添加了更多概念,例如 std::incrementable 等。请查看 标准库概念作为一个很好的起点。

C++20

I guess you want to check whether a user-provided type has equality operator or not; if that is the case then Concepts are here to help.

#include <concepts>

struct S{
   int x;
};

template<std::equality_comparable T>
bool 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 !=
}

If you pass any type that does not have == and != defined, the compiler just errors out with message, e.g.:

equality_comparable concept not satisfied by type


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

There are many more concepts that have been added to standards such as std::incrementable etc.. Have a look at Standard Library concepts as a good starting point.

雪若未夕 2024-11-24 21:38:27

从 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&>()()));
}

As of c++14, the standard binary functions do most of the work for us for the majority of 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&>()()));
}
来世叙缘 2024-11-24 21:38:27

我知道这个问题早已得到解答,但我认为对于将来发现这个问题的任何人来说,Boost 刚刚在其 type_traits 库中添加了一堆“has 运算符”特征,其中包括 has_equal_to,它满足了OP的要求。

I know this question has long since been answered but I thought it might be worth noting for anyone who finds this question in the future that Boost just added a bunch of "has operator" traits to their type_traits library, and among them is has_equal_to, which does what OP was asking for.

一束光,穿透我孤独的魂 2024-11-24 21:38:27

这个问题已经被回答过几次了,但是有一种更简单的方法来检查 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 功能。当然,最终结果是相同的,但这已成为我测试此类事物的首选习惯用法。

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

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.

洒一地阳光 2024-11-24 21:38:27

c++17 稍作修改的 Richard Hodges 版本 godbolt

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

c++17 slightly modified version of Richard Hodges godbolt

#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>);
意中人 2024-11-24 21:38:27

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

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

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

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

R 的默认值是 bool code> 表示这是一般情况。如果 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

在线演示

希望有帮助。

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

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

However, that might not be good enough for some corner cases. For example, say your class X does define operator== but it doesn't return bool, instead it returns Y. So in this case, what should equality<X>::value return? true or false? Well, that depends on the specific use case which we dont know now, and it doesn't seem to be a good idea to assume anything and force it on the users. However, in general we can assume that the return type should be bool, so lets express this in the interface itself:

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

The default value for R is bool which indicates it is the general case. In cases, where the return type of operator== is different, say Y, then you can say this:

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

which checks for the given return-type as well. By 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 that helps.

瞄了个咪的 2024-11-24 21:38:27

除了@coder3101答案之外,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>;
    };

阅读有关 C++20 中概念的更多信息。

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.

云之铃。 2024-11-24 21:38:27

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

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;

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;
和影子一齐双人舞 2024-11-24 21:38:27

这应该适用于 C++11

template <class Void, template<class...> class Type, class... Args>
struct validator
{
     using value_t = std::false_type;
};

template <template<class...> class Type, class... Args>
struct validator< std::void_t<Type<Args...>>, Type, Args... >
{
     using value_t = std::true_type;
};
 
template <template<class...> class Type, class... Args>
using is_valid = typename validator<void, Type, Args...>::value_t;

template<typename... T>
using has_equal_t = decltype((std::declval<T&>().operator ==(std::declval<T&>()), ...));

template<typename... T>
using has_gequal_t = decltype((operator ==(std::declval<T&>(),std::declval<T&>()), ...));


struct EQ
{
     bool operator==(const EQ&) const;
};

struct GEQ
{
};
bool operator==(const GEQ&, const GEQ&);

struct NOEQ
{
};

static_assert(is_valid<has_equal_t,EQ>::value || is_valid<has_gequal_t,EQ>::value, "should have equal operator");
static_assert(is_valid<has_equal_t,GEQ>::value || is_valid<has_gequal_t,GEQ>::value, "should have equal operator");
// static_assert(is_valid<has_equal_t,NOEQ>::value || is_valid<has_gequal_t,NOEQ>::value, "should have equal operator"); // ERROR:

This should work on C++11

template <class Void, template<class...> class Type, class... Args>
struct validator
{
     using value_t = std::false_type;
};

template <template<class...> class Type, class... Args>
struct validator< std::void_t<Type<Args...>>, Type, Args... >
{
     using value_t = std::true_type;
};
 
template <template<class...> class Type, class... Args>
using is_valid = typename validator<void, Type, Args...>::value_t;

template<typename... T>
using has_equal_t = decltype((std::declval<T&>().operator ==(std::declval<T&>()), ...));

template<typename... T>
using has_gequal_t = decltype((operator ==(std::declval<T&>(),std::declval<T&>()), ...));


struct EQ
{
     bool operator==(const EQ&) const;
};

struct GEQ
{
};
bool operator==(const GEQ&, const GEQ&);

struct NOEQ
{
};

static_assert(is_valid<has_equal_t,EQ>::value || is_valid<has_gequal_t,EQ>::value, "should have equal operator");
static_assert(is_valid<has_equal_t,GEQ>::value || is_valid<has_gequal_t,GEQ>::value, "should have equal operator");
// static_assert(is_valid<has_equal_t,NOEQ>::value || is_valid<has_gequal_t,NOEQ>::value, "should have equal operator"); // ERROR:
套路撩心 2024-11-24 21:38:27

仅供参考,我发布了如何解决问题的方法,无需检查 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;
}

Just for a reference, I am posting how I solved my problem, without a need to check if the 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==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;
}
漫雪独思 2024-11-24 21:38:27

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

IMO, this must be part of the class itself as it's deals with the private attributes of the class. The templates are interpreted at compile time. By default it generates operator==,constructor, destructor and copy constructor which do bit-wise copy (shallow copy) or bit-wise comparisons for the object of same type. The special cases (different types) must be overloaded. If you use global operator function you will have to declare the function as friend to access the private part or else you've to expose the interfaces required. Sometimes this is really ugly which may cause an unnecessary expose of a function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文