Reputation: 77
I am new to template programming and I intend to use the solution here to make sure that the types used have an operator defined. But I would like to understand this code. I looked up information of cppreference but I am more confused on how this works.
How to check whether operator== exists?
Unfortunately it is quite cryptic to me and thought would ask for the meaning and reasons for some of the things in the code.
namespace CHECK
{
struct No {};
template <typename T, typename Arg>
No operator==(const T&, const Arg&);
/* Why are there two definitions of structures? Is this needed at all? Is
* there a simpler version of this whole code snippet?
*/
template <typename T, typename Arg = T>
struct EqualExists {
/* Why does this have to be a enum? What effect does this have? */
enum {
/* What happens if == sign is not defined for T? What would the
* below comparison return when true/false? (The comparison *(T*)(0) == *(Arg*)(0))
*
* What is the need for No here? Why is it needed and how will
* comparing it with the return type of == comparison be true ever?
* How can the return type of == comparison and No structure be the same?
*/
value = !std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value
};
};
}
Could someone link this to the original question? Hope this helps someone new to cpp to understand this as well.
Upvotes: 0
Views: 130
Reputation: 774
Let's break it down step-by-step:
(T*)(0)
: pretend there's an object of type T
at address 0; the type of this subexpression is a pointer*(T*)(0)
: pretend there's an object of type T
at address 0; the type of this subexpression is a reference, due to the asterisk in front dereferencing the pointer. It looks kind of like a null pointer dereference, but more on that later.*(Arg*)(0)
: pretend there's an object of type Arg
at address 0; same pattern as T*(T*)(0) == *(Arg*)(0)
: this is equivalent to calling operator==<T, Arg>( *(T*)(0), *(Arg*)(0) )
with the additional convenience of letting the compiler figure out where the operator is defined.
operator==
does not exist, then the CHECK
namespace operator will be matched instead. It's a "catch all" template.decltype(*(T*)(0) == *(Arg*)(0))
: decltype
says "don't execute the subexpression in the parentheses; just give me its type". In this case, the type is the return type of the operator==
call.
std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value
: std::is_same
's value
is true
if the types are identical, or false
otherwise.
::value
is a static constexpr booldecltype(...)
and struct CHECK::No
.operator==
typically returns bool. Occasionally it may return a user-defined type. It's unlikely that someone will write their custom operator==
to return CHECK::No
, and this code is relying upon that assumption.enum { value = !std::is_same<...>::value }
: An enum is always a compile-time constant, works on older C++ compilers & specs (like C++03, where constexpr
didn't exist), is compatible with constexpr
, and does not require storage.
static constexpr bool value = !std::is_same<...>::value;
would have been equivalent.std::declval
is a safe alternative.CHECK::No operator==(const Foo&, const Bar&)
, which would fool the check into thinking the operator was undefined.operator==
in the CHECK namespace could shadow a globally defined operator== definition, resulting in a false negative.To fix the above issues and "simplify", one method is to use SFINAE. Although "simpler" is subjective here.
[edit] Link to working version: http://coliru.stacked-crooked.com/a/e9cc48729d53b6c6 , and updated code below
template <typename T, typename Arg = T>
class EqualExists {
template <class U = T, class V = Arg, bool Exists = !!sizeof(std::declval<U>() == std::declval<V>())>
static std::true_type Func(const T&, const Arg&);
template <class U, class V>
static std::false_type Func(const U&, const V&);
public:
static constexpr bool value = decltype(Func(std::declval<T>(), std::declval<Arg>()))::value;
};
[edit] My original answer had a bug : did not use template args U and V in the Exists computation, and did fail to compile on gcc. (worked on msvc for some reason)
template <class U, class V, bool Exists = !!sizeof(std::declval<T>() == std::declval<Arg>())>
static std::true_type Func(const T&, const Arg&);
Upvotes: 2