В C ++ 11 самым коротким и общим решением, которое я нашел, было следующее:
#include <type_traits>
template<class T, class = decltype(std::declval<T>() < std::declval<T>() )>
std::true_type supports_less_than_test(const T&);
std::false_type supports_less_than_test(...);
template<class T> using supports_less_than = decltype(supports_less_than_test(std::declval<T>()));
#include<iostream>
struct random_type{};
int main(){
std::cout << supports_less_than<double>::value << std::endl; // prints '1'
std::cout << supports_less_than<int>::value << std::endl; // prints '1'
std::cout << supports_less_than<random_type>::value << std::endl; // prints '0'
}
Работает с g++ 4.8.1
и clang++ 3.3
Aболее общее решение для произвольных операторов (ОБНОВЛЕНИЕ 2014)
Существует более общее решение, использующее тот факт, что все встроенные операторы также доступны (и, возможно, специализированы) через оболочки операторов STD, такие какstd::less
(двоичный) или std::negate
(одинарный).
template<class F, class... T, typename = decltype(std::declval<F>()(std::declval<T>()...))>
std::true_type supports_test(const F&, const T&...);
std::false_type supports_test(...);
template<class> struct supports;
template<class F, class... T> struct supports<F(T...)>
: decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};
Это может быть использовано в довольно общем виде, особенно в C ++ 14, где вычитание типа задерживается до вызова оболочки оператора («прозрачные операторы»).
Длябинарные операторы могут быть использованы как:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::less<>(double, double)>::value << std::endl; // '1'
std::cout << supports<std::less<>(int, int)>::value << std::endl; // '1'
std::cout << supports<std::less<>(random_type, random_type)>::value << std::endl; // '0'
}
Для унарных операторов:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::negate<>(double)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(int)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(random_type)>::value << std::endl; // '0'
}
(С стандартной библиотекой C ++ 11 немного сложнее, потому что нет ошибок наинсталируя decltype(std::less<random_type>()(...))
, даже если для random_type
не определена операция, можно реализовать прозрачные операторы вручную в C ++ 11, которые являются стандартными в C ++ 14)
Синтаксис довольно плавный.Я надеюсь, что что-то подобное будет принято в стандарте.
Два расширения:
1) Это работает для обнаружения приложений с необработанными функциями:
struct random_type{};
random_type fun(random_type x){return x;}
int main(){
std::cout << supports<decltype(&fun)(double)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(int)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(random_type)>::value << std::endl; // '1'
}
2) Он может дополнительно определить, является ли результат конвертируемым / сопоставимым с определенным типом, в этом случае поддерживается double < double
, но будет возвращено ложное время компиляции, поскольку результат не является указанным.
std::cout << supports<std::equal_to<>(std::result_of<std::less<>(double, double)>::type, random_type)>::value << std::endl; // '0'
Примечание. Я только что попытался скомпилировать код с C ++ 14 в http://melpon.org/wandbox/, и он не работал.Я думаю, что есть проблема с прозрачными операторами (например, std::less<>
) в этой реализации (clang ++ 3.5 c ++ 14), поскольку, когда я реализую свой собственный less<>
с автоматическим удержанием, он работает хорошо.