
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s. This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous. If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback! For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity. - Hui Li #include <utility> #include <type_traits> template < typename T > struct is_callable; template < typename Signature > struct has_valid_call; template < typename Signature > struct has_ambiguous_call; template < typename Signature > struct has_no_viable_call; template < typename T > struct is_callable { struct yes {}; struct no {}; struct ambiguate_seed { void operator()(); }; template < typename U, bool=std::is_class<U>::value > struct ambiguate : U, ambiguate_seed {}; template < typename U > struct ambiguate<U,false> : ambiguate_seed {}; template < typename U, typename = decltype(&U::operator()) > static constexpr no test(int); template < typename > static constexpr yes test(...); static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ; typedef std::integral_constant<bool,value> type; }; template < typename T, typename... Args > struct has_valid_call< T(Args...) > { struct yes {}; struct no {}; template < typename U, bool = is_callable<U>::value > struct impl { template < typename V, typename = typename std::result_of<V(Args...)>::type > struct test_result { using type = yes; }; template < typename V > static constexpr typename test_result<V>::type test(int); template < typename > static constexpr no test(...); static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value; using type = std::integral_constant<bool, value>; }; template < typename U > struct impl<U,false> : std::false_type {}; static constexpr bool value = impl<T>::value; using type = std::integral_constant<bool, value>; }; template < typename T, typename... Args > struct has_ambiguous_call< T(Args...) > { struct ambiguate_seed { void operator()(...); }; template < class U, bool = is_callable<U>::value > struct ambiguate : U, ambiguate_seed { using ambiguate_seed::operator(); using U::operator(); }; template < class U > struct ambiguate<U,false> : ambiguate_seed {}; static constexpr bool value = not has_valid_call< ambiguate<T>(Args...) >::value; using type = std::integral_constant<bool, value>; }; template < typename T, typename... Args > struct has_no_viable_call< T(Args...) > : std::integral_constant<bool, not (has_valid_call<T(Args...)>::value or has_ambiguous_call<T(Args...)>::value)> {}; #include <string> #include <vector> struct test { void operator()(int); void operator()(double); void operator()(int,double); template < typename T > typename std::enable_if< not std::is_integral<T>::value >::type operator()(const T&, int=0){} template < typename T > typename std::enable_if< std::is_integral<T>::value >::type operator()(const std::vector<T>&, T*){} template < typename T > int operator()(const std::string&, int){} }; int main(int argc, const char * argv[]) { static_assert( is_callable<test>::value , ""); static_assert( not is_callable<int>::value , ""); static_assert( has_valid_call<test(char const*,long)>::value , ""); static_assert( has_valid_call<test(std::string&,long)>::value , ""); static_assert( has_valid_call<test(std::vector<int>, int*)>::value , ""); static_assert( has_no_viable_call<test(std::vector<double>, double*)>::value , ""); static_assert( has_valid_call<test(int)>::value , ""); static_assert( has_valid_call<test(int,double)>::value , ""); static_assert( not has_valid_call<test(int,double,int)>::value , ""); static_assert( not has_ambiguous_call<test(double)>::value , ""); static_assert( has_ambiguous_call<test(unsigned)>::value , ""); static_assert( not has_no_viable_call<test(unsigned)>::value , ""); static_assert( not has_no_viable_call<test(int)>::value , ""); static_assert( has_no_viable_call<test(void)>::value , ""); return 0; }