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
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 : 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(0)),yes>::value ;
typedef std::integral_constant 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::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::value;
using type = std::integral_constant;
};
template < typename U >
struct impl : std::false_type {};
static constexpr bool value = impl<T>::value;
using type = std::integral_constant;
};
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 : ambiguate_seed {};
static constexpr bool value = not has_valid_call< ambiguate<T>(Args...) >::value;
using type = std::integral_constant;
};
template < typename T, typename... Args >
struct has_no_viable_call< T(Args...) >
: std::integral_constant::value or has_ambiguous_call::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::value , "");
static_assert( has_valid_call::value , "");
static_assert( has_valid_call::value , "");
static_assert( has_no_viable_call::value , "");
static_assert( has_valid_call::value , "");
static_assert( has_valid_call::value , "");
static_assert( not has_valid_call::value , "");
static_assert( not has_ambiguous_call::value , "");
static_assert( has_ambiguous_call::value , "");
static_assert( not has_no_viable_call::value , "");
static_assert( not has_no_viable_call::value , "");
static_assert( has_no_viable_call::value , "");
return 0;
}