I'm writing a template function for shuffling, and I want to check whether the 'less than' operator is overloaded on an arbitrary data structure before trying to use it. It this possible?
We can use the Detection Idiom to test whether T < T
is well formed at compile time.
For readability, I'm using experimental::is_detected, but you can roll your own in C++11 with the voider pattern.
First, a class it works for which a <
is well formed:
struct Has_Less_Than{
int value;
};
bool operator < (const Has_Less_Than& lhs, const Has_Less_Than& rhs) {return lhs.value < rhs.value; }
And then one where it isn't:
struct Doesnt_Have_Less_Than{
int value;
};
// no operator < defined
Now, for the detection idiom part: we try to get the type of the result of a "theoretical" comparison, and then ask is_detected
:
template<class T>
using less_than_t = decltype(std::declval<T>() < std::declval<T>());
template<class T>
constexpr bool has_less_than = is_detected<less_than_t, T>::value;
int main()
{
std::cout << std::boolalpha << has_less_than<Has_Less_Than> << std::endl; // true
std::cout << std::boolalpha << has_less_than<Doesnt_Have_Less_Than> << std::endl; // false
}
Live Demo
If you have C++17 available, you can take advantage of constexpr if for your test:
if constexpr(has_less_than<Has_Less_Than>){
// do something with <
}
else{
// do something else
}
It works because constexpr if is evaluated at compile-time, and the compiler will only compile the branch that was taken.
If you don't have C++17 available, you'll need to use a helper function, perhaps with tagged dispatch:
template<class T>
using less_than_t = decltype(std::declval<T>() < std::declval<T>());
template<class T>
using has_less_than = typename is_detected<less_than_t, T>::type;
template<class T>
void do_compare(const T& lhs, const T& rhs, std::true_type) // for operator <
{
std::cout << "use operator <\n";
}
template<class T>
void do_compare(const T& lhs, const T& rhs, std::false_type)
{
std::cout << "Something else \n";
}
int main()
{
Has_Less_Than a{1};
Has_Less_Than b{2};
do_compare(a, b, has_less_than<Has_Less_Than>{});
Doesnt_Have_Less_Than c{3};
Doesnt_Have_Less_Than d{4};
do_compare(c, d, has_less_than<Doesnt_Have_Less_Than>{});
}
Demo
If you have C++20 available, we can easily accomplish this with a concept:
template<class L, class R=L>
concept has_less_than = requires(const L& lhs, const R& rhs)
{
{lhs < rhs} -> std::same_as<bool>;
};
This concept can be read as "Given two possibly different types, the concept is true
if calling operator<
between two const references returns a boolean. Otherwise it is false."
You could fiddle with it a bit, e.g., changing same_as
to convertible_to
(if for some reason you expected operator<
to return an integer that could be converted to a boolean.
C++20 Demo
The concept can be used instead of enable_if
(requires my_concept<T>
), and is also convertible to a compile-time boolean value like true_type
and false_type
are (so it could be used in if constexpr
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With