我正在尝试创建一个示例,它将检查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==
的存在?
如果是,怎么样?
这就是我的尝试:
template <typename C> static yes test( const C*,bool(*)(const C&,constC&) = &operator== );
但如果非成员运算符==被删除,则编译失败
答案 0 :(得分:37)
以下技巧有效。它可以用于所有这些运营商:
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==`
当我们有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(*(T*)(0) == *(Arg*)(0)), No>::value };
};
}
答案 1 :(得分:16)
查看 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!=
。
答案 2 :(得分: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 {};
你可以像这样使用这个特性:
bool test = has_operator_equal<MyClass>::value;
结果类型的has_operator_equal
将是std::true_type
or 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)
,long
或任何可隐式转换为的值都属于long
,此特征属于double
1}},例如int
,decltype
等
您还可以定义对其他运算符和函数的检查,只需替换!=
中的内容即可。要检查static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());
,只需替换
static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
与
$('#iframe1').contents().find('#iframe2').contents().find('#btn1')
答案 3 :(得分:3)
从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&>()()));
}
答案 4 :(得分:2)
这个问题已经多次得到解答,但有一种更简单的方法可以检查是否存在 ocamlfind list | grep llvm
或基本上任何其他操作(例如,测试具有特定名称的成员函数),使用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
我认为这使得代码的意图更加清晰。除此之外,这是一个C ++ 11解决方案,因此它不依赖于任何较新的C ++ 14或C ++ 17功能。当然,最终的结果是一样的,但这已成为我测试这类事物的首选习惯。
编辑:修复了重载逗号运算符的疯狂情况,我总是想念它。
答案 5 :(得分:1)
我知道这个问题早已得到解答,但我认为,对于今后发现这个问题的人来说,Boost只是在他们的type_traits库中添加了一堆“has operator”特征,其中包括has_equal_to,这正是OP所要求的。
答案 6 :(得分:1)
我想您想检查用户提供的Template类型是否具有相等运算符,如果是这种情况,这里的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
等标准中已经添加了更多概念。请看here
答案 7 :(得分:0)
IMO,这必须是类本身的一部分,因为它处理类的私有属性。模板在编译时解释。默认情况下,它生成operator==
,构造函数,析构函数和复制构造函数,它们对相同类型的对象进行逐位复制(浅复制)或逐位比较。特殊情况(不同类型)必须重载。如果使用全局运算符函数,则必须将该函数声明为访问私有部分的朋友,否则您将公开所需的接口。有时候这很丑陋,可能导致不必要的函数暴露。
答案 8 :(得分: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;
}
答案 9 :(得分:0)
让我们考虑以下形式的元函数,它检查给定类型是否存在等于运算符(即==
):
template<typename T>
struct equality { .... };
然而,对于某些角落案例而言,这可能不够好。例如,假设您的班级X
确实定义了operator==
,但它不会返回bool
,而是返回Y
。那么在这种情况下,equality<X>::value
应该返回什么? true
或false
?嗯,这取决于我们现在不知道的具体用例,并且假设任何东西并强制它在用户身上似乎不是一个好主意。但是,通常我们可以假设返回类型应该是bool
,所以让我们在界面本身表达:
template<typename T, typename R = bool>
struct equality { .... };
R
的默认值为bool
,表示这是一般情况。如果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
希望有所帮助。
答案 10 :(得分:0)
除了@coder3101 answer,concepts
还可以帮助您实现所需的任何功能存在测试。例如,std::equality_comparable
是使用4个简单的测试实现的,这些测试检查以下情况:
对于A
和B
变量,请确保以下表达式有效:
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>;
};
答案 11 :(得分:0)
我们可以使用std::equal_to<Type>
(或任何其他重载的struct成员)提出更通用的解决方案。
struct No {};
template<class T, class Operator>
struct ExistsOperator
{
enum { value = !std::is_same<decltype(std::declval<Operator>()(std::declval<T>(), std::declval<T>())), No>::value };
};
用法:
using Type = int;
constexpr bool hasEqual = ExistsOperator<Type, std::equal_to<Type>>::value;