
Once you know that operator< exists and returns non-void, you can use simple overload resolution.
typedef char no; struct yes { no dummy[2]; };
yes is_bool(bool); no is_bool(...);
sizeof(is_bool(T() < U())) == sizeof(yes);
That assumes T and U have a default constructor. Use a factory function to create T's and U's instead.
Very nice but I still cannot handle "convertible to bool". I have tried to use boost::is_convertible but could not. Here is my code and you can see that StrangeComparable objects give the bad result. Also I have manually entered all built-in types convertible to bool so that they are handled properly but new types convertible to bool are not handled. Frédéric #include <iostream> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> namespace boost { namespace detail { // This namespace ensures that ADL doesn't mess things up. namespace is_less_comparable_impl { // a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ; // any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ; tag operator<(const any&, const any&) ; // two check overloads help us identify which comparison operator was picked // check(not convertible to bool) returns a reference to char[2] (sizeof>1) template <typename T> char (& check(T))[2] ; // check(convertible to bool) returns char (sizeof==1) template <typename T> char check(T*) ; char check(bool) ; char check(char) ; char check(signed char) ; char check(unsigned char) ; char check(short) ; char check(unsigned short) ; char check(int) ; char check(unsigned int) ; char check(long) ; char check(unsigned long) ; template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(check(t<u))==1 ; } ; } // namespace impl } // namespace detail template< typename T, typename U=T > struct is_less_comparable : boost::integral_constant<bool,(::boost::detail::is_less_comparable_impl::is_less_comparable_impl<T,U>::value)> { } ; } // namespace boost // for testing struct NotComparable { } ; struct Comparable { bool operator<(const Comparable&) const ; } ; struct IntComparable { int operator<(const IntComparable&) ; } ; struct PtrComparable { void *operator<(const PtrComparable&) ; } ; // this one has a strange operator<: it returns a non standard type convertible to bool struct Convertible { operator bool () const ; } ; struct StrangeComparable { Convertible operator<(const StrangeComparable&) ; } ; #define TEST1(T)\ std::cout << boost::is_less_comparable<T, T>::value << "\t(" << #T << '<' << #T << ")\n" #define TEST2(T1,T2)\ std::cout << boost::is_less_comparable<T1, T2>::value << "\t(" << #T1 << '<' << #T2 << ")\n" ;\ std::cout << boost::is_less_comparable<T2, T1>::value << "\t(" << #T2 << '<' << #T1 << ")\n" int main() { std::cout << std::boolalpha ; TEST1(NotComparable) ; TEST1(Comparable) ; TEST1(IntComparable) ; TEST1(PtrComparable) ; TEST1(StrangeComparable) ; TEST2(double, NotComparable) ; TEST2(int, NotComparable) ; TEST2(double, IntComparable) ; TEST2(int, IntComparable) ; TEST2(double, Comparable) ; TEST2(int, Comparable) ; TEST2(int, char) ; TEST2(int, short) ; TEST2(int, long) ; TEST2(int, float) ; TEST2(int, double) ; return 0 ; }