5

How to static_assert a template type is EqualityComparable concept in C++11?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
eonil
  • 83,476
  • 81
  • 317
  • 516
  • 2
    Note that `static_assert` on template type arguments is sometimes a suboptimal idea. You'd often rather fail to match, which requires SFINAE, instead of fail to compile, which `static_assert` does. – Yakk - Adam Nevraumont May 06 '13 at 14:35

1 Answers1

21

You could use the following type trait:

#include <type_traits>

template<typename T, typename = void>
struct is_equality_comparable : std::false_type
{ };

template<typename T>
struct is_equality_comparable<T,
    typename std::enable_if<
        true, 
        decltype(std::declval<T&>() == std::declval<T&>(), (void)0)
        >::type
    > : std::true_type
{
};

Which you would test this way:

struct X { };
struct Y { };

bool operator == (X const&, X const&) { return true; }

int main()
{
    static_assert(is_equality_comparable<int>::value, "!"); // Does not fire
    static_assert(is_equality_comparable<X>::value, "!"); // Does not fire
    static_assert(is_equality_comparable<Y>::value, "!"); // Fires!
}

Here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 1
    Minor correction: It won't work properly if T is not default constructible - make it `std::declval()` instead of `T()`. – jrok May 06 '13 at 13:46
  • @jrok: Absolutely. Don't know how I could overlook that. Thank you – Andy Prowl May 06 '13 at 13:49
  • Strictly saying, it's not 100% EqualityComparable concept checking, because Commutativity and Transitivity reqs aren't checked. But as they cannot be checked in compile time, it's not a big deal, I guess. – Rost May 06 '13 at 14:01
  • Note that your check assumes `operator==` accepts rvalues - use `std::declval()` for lvalues. – Xeo May 06 '13 at 14:02
  • I understand that it doesn't make much sense to have an equality operator accepting rvalues, but what is the point of using `std::declval()` to force this restriction in the `is_equality_comparable` struct? Wouldn't it be better to have this generic code to work, regardless of how the user implemented his `operator==`? Am I missing something? – ChristopherC Dec 19 '14 at 07:53
  • 1
    @ChristopherC: Whichever you choose (`declval` or `declval`) there will always be a "non-idiomatic" signature that the trait is not going to recognize correctly. If I picked `declval`, types with an `operator ==` taking *modifiable* lvalue references would be recognized as non-equality-comparable. Granted, taking by modifiable lvalue reference makes little sense, but so does (or even less) taking by rvalue reference. – Andy Prowl Dec 19 '14 at 10:43
  • Thanks @AndyProwl and @Xeo! This helped me out going into the right direction, towards answering [this question](http://stackoverflow.com/questions/27590890/how-to-check-at-compile-time-if-a-function-that-can-be-called-with-a-specific-se). – ChristopherC Dec 21 '14 at 16:09
  • declval return a T && anyway (https://en.cppreference.com/w/cpp/utility/declval), so there is no difference between declval and declval. – Matthias May 19 '19 at 21:52
  • 2
    The std::enable_if is superfluous. The specialization can be simplified to template struct is_equality_comparable< T, decltype(std::declval() == std::declval(), (void)0)> : std::true_type {}; – Matthias May 19 '19 at 21:56