Related
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
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
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.
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>());
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.
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&>()()));
}
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.
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.
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.
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>);
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.
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;
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:
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;
}
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.
This is a follow-up to my previous question.
I have a class with a cast operator to anything. In a pre-C++17 environment this yields errors of being unable to select appropriate constructor overload while performing initialization. I want to tune the behavior by marking the cast operator explicit for some types. However, I cannot find a way to do so.
Here is an artificial example: I want an implicit cast operator to integer types and explicit to all other types.
This doesn't work because we cannot determine U having the expression of type typename std::enable_if<!std::is_integral<U>::value, U>::type:
struct C {
template<typename U>
operator typename std::enable_if< std::is_integral<U>::value, U>::type() const {
return 1;
}
template<typename U>
explicit operator typename std::enable_if<!std::is_integral<U>::value, U>::type() const {
return 1.5;
}
};
This one fails to compile saying that C::operator U() cannot be overloaded:
struct C {
template<typename U, typename = typename std::enable_if< std::is_integral<U>::value, U>::type>
operator U() const {
return 1;
}
template<typename U, typename = typename std::enable_if<!std::is_integral<U>::value, U>::type>
explicit operator U() const {
return 1.5;
}
};
I cannot declare the function of kind template<typename U, typename = void> operator U(); and partially specialize it because partial function specialization is not allowed and making a helper class looks like an overkill to me.
How can I declare cast operator based on some traits of the type I'm casting to?
I need a C++11 solution, as in C++17 the issue from my previous question is already resolved.b
You can move definitions of these operators to the base classes. This approach allows you put constraints on both implicit and explicit operators:
#include <type_traits>
#include <iostream>
template<typename TDerived> class
t_ImplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<::std::is_integral<TTarget>::value>
>
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_Integral<TTarget>());
}
};
template<typename TDerived> class
t_ExplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<!::std::is_integral<TTarget>::value>
> explicit
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_NonIntegral<TTarget>());
}
};
class
t_ConvertableToAnything
: public t_ImplicitlyConvertableToAnything<t_ConvertableToAnything>
, public t_ExplicitlyConvertableToAnything<t_ConvertableToAnything>
{
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_Integral(void) const
{
return(static_cast<TTarget>(1));
}
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_NonIntegral(void) const
{
return(static_cast<TTarget>(3.14));
}
};
int main()
{
t_ConvertableToAnything c;
::std::cout << ([](int x){return(x);})(c) << ::std::endl;
::std::cout << static_cast<float>(c) << ::std::endl;
return(0);
}
Run this code online
You can use non-type template parameters to avoid the "cannot be overloaded" issue:
#include <iostream>
#include <type_traits>
struct A { };
struct B { };
struct C {
template <typename U,
typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1;
}
template<typename U,
typename std::enable_if<std::is_same<U, A>::value>::type* = nullptr>
explicit operator U() const {
return A{ };
}
template<typename U,
typename std::enable_if<std::is_same<U, B>::value>::type* = nullptr>
explicit operator U() const {
return B{ };
}
};
int main() {
C c;
long y = static_cast<int>(c);
B b = static_cast<B>(c);
A a = static_cast<A>(c);
}
https://ideone.com/smfPwF
You can overload your cast operator using a trick with dummy template parameters for disambiguation.
struct C {
template<typename U,
typename = typename enable_if<is_integral<U>::value, U>::type,
int = 0> // <== hete
operator U() const {
return 1;
}
template<typename U,
typename = typename enable_if<!is_integral<U>::value, U>::type,
char = 0> // <== and here
explicit operator U() const {
return 1.5;
}
};
Since the template signatures are now different, there is no ambiguity.
Try this. Just leave off the constraints on the explicit operator since it covers all cases that the first operator does not.
Coliru example: http://coliru.stacked-crooked.com/a/3d0bc6e59ece55cf
#include <iostream>
#include <type_traits>
struct C {
template <typename U,
typename = typename std::enable_if< std::is_integral<U>::value>::type>
operator U() const {
return 1;
}
template<typename U, typename std::enable_if<!std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1.5;
}
};
int main() {
C c;
int v = c;
int w = c;
int x = static_cast<int>(c);
long y = static_cast<int>(c);
double z = static_cast<double>(c);
std::cout << v << std::endl;
std::cout << w << std::endl;
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << z << std::endl;
}
Thanks to #Jodocus for enabling explicit casts to integral types.
template<class T>
struct gSorting : public std::binary_function<T, T,bool> {
bool operator() (int number, int n2)
{
cout << "int" << endl;
return (number<n2);
}
bool operator() (double number, double n2)
{
cout << "double" << endl;
return (number<n2);
}
bool operator() (const MYClass& obj1, const MYClass& obj2)
{
return (obj1.element<obj2.element);
}
};
int main () {
gSorting<int> sorting_object;
std::cout << std::boolalpha << sorting_object (2.0f, 4.3f) << ".\n";
std::getchar();
return 0;
}
is there any problem with this code? is it that generic? or is there better way to do a generic sorting algorithm to include all of my classes used
it compiles, the output pointed to the double which is good, however how can I make it a template, but dont have to specify the input type in the declaration?
gSorting< int > sorting_object;
-------------^^^^ we dont need any specific type? am I right
output:
I would personally define a class template for the binary predicate and specialize it as needed, e.g.:
template <typename T>
struct gSorting
: std::binary_function<T const&, T const&, bool> // not really needed
{
bool operator()(T const& t0, T const& t1) const {
return t0 < t1;
}
};
template <>
struct gSorting<MyClass>
: std::binary_function<MyClass const&, MyClass const&, bool>
{
bool operator()(MyClass const& c0, MyClass const& c1) const {
return c0.element < c1.element;
}
};
In a real implementation, the argument type for the generic version should probably decide whether the argument is passed by value or by const& depending on the kind of type and/or based on a trait which is then specialized as needed. For example:
template <typename T>
struct argument_type
{
typedef typename std::conditional<
std::is_fundamental<T>::value, T, T const&>::type type;
};
template <typename T>
struct gSorting
: std::binary_function<typename argument_type<T>::type,
typename argument_type<T>::type, bool>
{
typedef typename argument_type<T>::type arg_type;
bool operator()(arg_type t0, arg_type t1) const {
return t0 < t1;
}
};
I want to define a function template<typename T> T constCast(const ScriptVar_t& s);. Depending on T, I want to have different definitions. (ScriptVar_t is a class but details are not important here in this context.)
The conditions on T aren't as simple as specific types, they are all somewhat more complicated static boolean expressions. I.e. I have a list of expressions ext1..extN and for each, I have a definition of that function. And I want to have them checked in that order and the definition of the first matching expression should be used. If all of them fail, I want to get a compiler error.
Right now, I just have 2 definitions and my code looks like this (this is a full test case, the relevant code is marked):
#include <boost/type_traits.hpp>
enum {
SVT_INT,
SVT_FLOAT,
SVT_BASEOBJ,
SVT_CUSTOMVAR
};
struct BaseObject {};
struct CustomVar {};
template<typename T> struct GetType;
template<> struct GetType<int> { static const int value = SVT_INT; };
template<> struct GetType<float> { static const int value = SVT_FLOAT; };
template<> struct GetType<BaseObject> { static const int value = SVT_BASEOBJ; };
template<bool> struct GetType_BaseCustomVar;
template<> struct GetType_BaseCustomVar<true> {
struct Type { static const int value = SVT_CUSTOMVAR; };
};
template<typename T> struct GetType : GetType_BaseCustomVar<boost::is_base_of<CustomVar,T>::value>::Type {};
struct ScriptVar_t;
template<typename T> T CastScriptVarConst(const ScriptVar_t& s);
struct ScriptVar_t {
operator int() const { return 0; }
operator float() const { return 0.0f; }
operator BaseObject() const { return BaseObject(); }
template<typename T> T* as() const { return NULL; }
template <typename T> T castConst() const { return CastScriptVarConst<T>(*this); }
};
// *** relevant code starts here
template<typename T> T CastScriptVarConst(const ScriptVar_t& s);
template<bool> struct CastScriptVar1;
template<typename T> struct CastScriptVar1_IsSimpleType {
static const bool value = GetType<T>::value < SVT_BASEOBJ;
};
template<> struct CastScriptVar1<true> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return (T) s; }
};
template<bool> struct CastScriptVar2;
template<typename T> struct CastScriptVar2_IsCustomVar {
static const bool value = boost::is_base_of<CustomVar,T>::value;
};
template<> struct CastScriptVar2<true> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return *s.as<T>(); }
};
template<> struct CastScriptVar1<false> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) {
return CastScriptVar2<CastScriptVar2_IsCustomVar<T>::value>::castConst(s, T());
}
};
template<typename T> T CastScriptVarConst(const ScriptVar_t& s) {
return CastScriptVar1<CastScriptVar1_IsSimpleType<T>::value>::castConst(s, T());
}
int main() {
ScriptVar_t v;
v.castConst<int>();
v.castConst<CustomVar>();
}
I came up with this after a few tries until it works.
(As you can see from the code, the two expressions are GetType<T>::value < SVT_BASEOBJ and boost::is_base_of<CustomVar,T>::value. If both are false, the compiler should throw an error. But this is only an example for my question.)
I wonder if there is a somewhat more clean solution for this code.
For reference, I am playing with it here. And right now, I have again a somewhat different solution to all the other solutions here.
So if I understood correctly, you have two methods of casting. If GetType<T>::value < SVT_BASEOBJ then you just want to use a normal cast: (T) s;
On the other hand, if GetType<T>::value < SVT_BASEOBJ is false then you want to ensure that the CustomVar is a base class of type T (i.e., T derives from CustomVar). Then you want to use a member function on that object: *s.as<T>()
Otherwise you want a compile error.
Here's one way to do that using overloading and std::enable_if:
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return *s.as<T>();
}
enable_if takes advantage of the SFINAE rule in C++ so that if the conditions fail the only consequence is that the function is not added to the set of viable overloads for that call. Since the enable_if conditions are mutually exclusive at most one of the function will ever be viable for any given call, and so there'll never be ambiguity. And if neither condition is true then you'll get a compile error saying it couldn't find a matching function.
#include <type_traits>
#include <iostream>
enum {
SVT_INT,
SVT_FLOAT,
SVT_BASEOBJ,
SVT_CUSTOMVAR
};
struct BaseObject {};
struct CustomVar {};
template<typename T> struct GetType;
template<> struct GetType<int> { static const int value = SVT_INT; };
template<> struct GetType<float> { static const int value = SVT_FLOAT; };
template<> struct GetType<BaseObject> { static const int value = SVT_BASEOBJ; };
template<bool> struct GetType_BaseCustomVar;
template<> struct GetType_BaseCustomVar<true> {
struct Type { static const int value = SVT_CUSTOMVAR; };
};
template<typename T> struct GetType : GetType_BaseCustomVar<std::is_base_of<CustomVar,T>::value>::Type {};
struct ScriptVar_t {
operator int() const { return 0; }
operator float() const { return 0.0f; }
operator BaseObject() const { return BaseObject(); }
template<typename T> T* as() const { return NULL; }
template <typename T> T castConst() const;
};
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
std::cout << "value < SVT_BASEOBJT\n";
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
std::cout << "CustomVar\n";
return *s.as<T>();
}
template <typename T>
T ScriptVar_t::castConst() const {
return CastScriptVarConst<T>(*this);
}
int main() {
ScriptVar_t v;
v.castConst<int>();
v.castConst<CustomVar>();
}
If I understood correctly, I'd use a look-up table for the
cast-functors and a meta-function to calculate the offset into the
table.
An alternative would be to use a type based look-up table consisting
of tags and functors. pick_cast would then choose the right tag
instead of an int. This might be easier to read if the decision
table becomes large.
#include <boost/type_traits.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/at.hpp>
struct BaseObject {};
struct CustomVar {};
namespace mpl = boost::mpl;
struct ScriptVar_t {
operator int() const { return 0; }
operator float() const { return 0.0f; }
operator BaseObject() const { return BaseObject(); }
template<typename T> T* as() const { return NULL; }
template <typename T> T castConst() const;
};
struct default_cast {
template<typename T>
T operator()(const ScriptVar_t& s) const { return (T) s; }
};
struct base_cast {
template<typename T>
T operator()(const ScriptVar_t& s) const { return *s.as<T>(); }
};
typedef mpl::vector< default_cast, base_cast > casts;
enum {
DEFAULT = 0,
BASE,
END_OF_ENUM
};
// pick the right cast for T
template<typename T>
struct pick_cast {
typedef typename mpl::if_< typename boost::is_base_of<CustomVar,T>::type,
mpl::int_<BASE>, mpl::int_<DEFAULT> >::type type;
};
template <typename T> T ScriptVar_t::castConst() const {
typedef typename mpl::at<casts, typename pick_cast<T>::type>::type func;
return func().template operator()<T>(*this);
}
int main() {
ScriptVar_t v;
v.castConst<int>();
v.castConst<CustomVar>();
}
I am not sure if I understand you correctly but this metaprogram could do what you need:
// Just a placeholder type for default template arguments
struct NoType { };
// Template to check whether a type is NoType. You can replace this with boost
// is_same<T, NoType> if you like.
template <typename T>
struct IsNoType {
static const bool value = false;
};
template <>
struct IsNoType<NoType> {
static const bool value = true;
};
// Template for matching the expressions and their corresponding types
// This could be done more nicely using variadic templates but no MSVC
// version supports them currently.
// You can specify up to 8 conditions and types. If you specify more,
// the code will break :) You can add more easily by just expanding the
// number of lines and parameters though.
template <
bool p0 = false, typename t0 = NoType,
bool p1 = false, typename t1 = NoType,
bool p2 = false, typename t2 = NoType,
bool p3 = false, typename t3 = NoType,
bool p4 = false, typename t4 = NoType,
bool p5 = false, typename t5 = NoType,
bool p6 = false, typename t6 = NoType,
bool p7 = false, typename t7 = NoType,
// This must not be changed/overriden/specified, it is used as a condition to stop the compiler loop, see below
bool stop = true, typename stopT = NoType
>
struct GetFirstMatchingType {
};
// Specialization when the first element in the expression list is true.
// When this happens, we just return the first type as the ::type typedef.
template <
typename t0,
bool p1, typename t1,
bool p2, typename t2,
bool p3, typename t3,
bool p4, typename t4,
bool p5, typename t5,
bool p6, typename t6,
bool p7, typename t7,
bool p8, typename t8
>
struct GetFirstMatchingType<true, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8> {
typedef t0 type;
// Check that the type is not NoType. If it is, it means all arguments are false and we should fail.
// In case of a non-C++11 compiler, you can throw any other compiler error here or use BOOST_STATIC_ASSERT
static_assert(!IsNoType<t0>::value, "No expression has been matched, don't know what type to return!");
};
// Specialization when the first type is false. If this happens, we cyclically rotate
// the sequence so that p0, t0 becomes p8, t8. The compiler keeps expanding this
// until it finds true as the first element. Note that this will always happen because
// the stop argument in the base template is set to true.
template <
typename t0,
bool p1, typename t1,
bool p2, typename t2,
bool p3, typename t3,
bool p4, typename t4,
bool p5, typename t5,
bool p6, typename t6,
bool p7, typename t7,
bool p8, typename t8
>
struct GetFirstMatchingType<false, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8> {
typedef typename GetFirstMatchingType<p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8, false, t0>::type type;
};
int main() {
// Evaluates to int myVar1 if int is 4 bytes, or __int32 myVar1 if __int32 is 4 bytes and int is not 4 bytes
GetFirstMatchingType<
sizeof(int) == 4, int,
sizeof(__int32) == 4, __int32
>::type myVar1;
// Evaluates to short myVar on my platform
GetFirstMatchingType<
sizeof(int) == 5, int,
sizeof(short) == 2, short
>::type myVar2;
// Also evaluates to short myVar on my platform
GetFirstMatchingType<
sizeof(int) == 5, int,
sizeof(short) == 2, short,
sizeof(int) == 4, int
>::type myVar3;
// Throws an error (error C2338: No expression has been matched, don't know what type to return!)
GetFirstMatchingType<
sizeof(int) == 5, int,
sizeof(long) == 5, long,
sizeof(short) == 3, short
>::type myVar4;
}
Tested on MSVC 2010 but it should work well on any C++ compliant compiler such as GCC or Clang.
EDIT
Here's an example of a solution to your question using the above code:
struct ScriptVar_t;
struct CastScriptVar1 {
template<typename T> static T castConst(const ScriptVar_t& s) { return (T) s; }
};
struct CastScriptVar2 {
template<typename T> static T castConst(const ScriptVar_t& s) { return *s.as<T>(); }
};
struct ScriptVar_t {
operator int() const { return 0; }
operator float() const { return 0.0f; }
operator BaseObject() const { return BaseObject(); }
template<typename T> T* as() const { return NULL; }
template <typename T> T castConst() const {
return GetFirstMatchingType<
!boost::is_base_of<CustomVar, T>::value, CastScriptVar1,
GetType<T>::value >= SVT_BASEOBJ, CastScriptVar2
// Add more conditions & casts here
>::type::castConst<T>(*this);
}
};
int main()
{
ScriptVar_t v;
v.castConst<int>();
v.castConst<CustomVar>();
return 0;
}
It could be rewritten using boost::tuple and boost::mpl to get rid of the weird variadic templates.
EDIT 2: it seems my previous EDIT disappeared, I put it back
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
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
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.
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>());
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.
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&>()()));
}
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.
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.
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.
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>);
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.
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;
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:
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;
}
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.