How to check at compile-time if a function that can be called with a specific set of arguments exists?

后端 未结 2 1143
独厮守ぢ
独厮守ぢ 2021-01-01 05:18

This is different from checking if a specific function is defined. Here, for this check to return true, the function has to be defined and passing the arguments

相关标签:
2条回答
  • 2021-01-01 05:56

    Making use of C++11, this can be done using a mixture of SFINAE, decltype and std::declval.

    template<typename ...>
    struct Bool
    { using type = bool; };
    
    template<typename ... T_Dummies>
    using BoolT = typename Bool<T_Dummies ...>::type;
    
    
    template<typename T>
    struct DeclvalType
    {
        using type = typename std::conditional<
            std::is_rvalue_reference<T>::value,
            T,
            T &
        >::type;
    };
    
    template<typename T>
    using DeclvalTypeT = typename DeclvalType<T>::type;
    
    
    template<typename T>
    struct ExtractFunction;
    
    template<typename T_Return, typename ... T_Args>
    struct ExtractFunction<T_Return(T_Args ...)>
    { using type = T_Return(T_Args ...); };
    
    template<typename T_Return, typename ... T_Args>
    struct ExtractFunction<T_Return(*)(T_Args ...)>
    { using type = T_Return(T_Args ...); };
    
    template<typename T, typename T_Return, typename ... T_Args>
    struct ExtractFunction<T_Return(T::*)(T_Args ...)>
    { using type = T_Return(T_Args ...); };
    
    template<typename T, typename T_Return, typename ... T_Args>
    struct ExtractFunction<T_Return(T::*)(T_Args ...) const>
    { using type = T_Return(T_Args ...); };
    
    template<typename T>
    using ExtractFunctionT = typename ExtractFunction<T>::type;
    
    
    template<typename ... T, typename T_Function>
    constexpr auto
    impl(T_Function function) ->
        BoolT<decltype(
            std::declval<ExtractFunctionT<T_Function>>()
                (std::declval<DeclvalTypeT<T>>() ...)
        )>
    { return true; }
    
    template<typename ... T>
    constexpr bool
    impl(...)
    { return false; }
    
    
    template<typename ... T, typename T_Function>
    constexpr bool
    isFunctionCallable(T_Function function)
    { return impl<T ...>(function); }
    

    With the help of some more code (available in this Gist), it is possible to output tables showing what type of arguments can be passed to what type of parameters.

    using T = Default (empty struct with implicit constructors):
    
      +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
      |                                |                                                                                                                                 |
      |        Function signature      |                                                          Argument type                                                          |
      |                                |                                                                                                                                 |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T)                   |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T)             |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T)          |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T)    |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
    
    
    using T = NonCopiable:
    
      +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
      |                                |                                                                                                                                 |
      |        Function signature      |                                                          Argument type                                                          |
      |                                |                                                                                                                                 |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T)                   |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T)             |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T)          |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T)    |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
    
    
    using T = NonMovable:
    
      +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
      |                                |                                                                                                                                 |
      |        Function signature      |                                                          Argument type                                                          |
      |                                |                                                                                                                                 |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T)                   |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T)             |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T)          |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T)    |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
    
    
    using T = NonCopiableNonMovable:
    
      +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
      |                                |                                                                                                                                 |
      |        Function signature      |                                                          Argument type                                                          |
      |                                |                                                                                                                                 |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T)                   |     |           |               |                     |       |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T)             |     |           |               |                     |       |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T)          |     |           |               |                     |       |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T)    |     |           |               |                     |       |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
      |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
      +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
    

    We can for example deduce from these tables that an argument of type T can't be passed to a function that takes T && as parameter. Or that function(T &&) only accepts arguments of type T &&.

    Note how deleting the copy and/or the move constructor reduce the possibilities since the arguments can't be converted implicitly anymore.

    Edit:

    Added support for member functions, thanks to @hvd.

    0 讨论(0)
  • 2021-01-01 06:08
    #define overload_set(F)\
      struct { auto operator()(auto&&...args)const\
        ->decltype(F(std::forward<decltype(args)>(args)...))\
         { return (F(std::forward<decltype(args)>(args)...)); }\
      }
    

    this takes a token F and generates an overload set type for F.

    It is not quite perfect: it only does perfect forwarding with SFINAE tests. But it is close.

    We then use this:

    template<class T,class=void>struct can_invoke:std::false_type{};
    template<class F,class...Args>
    struct can_invoke<F(Args...),
      decltype(void(
        std::declval<F>()(std::declval<Args>()...)
      ))
    >:std::true_type{};
    

    Mixing them we get:

    typedef overload_set(Foo) Foo_overloads;
    std::cout << can_invoke<Foo_overloads(int, int) >::value<<"\n";
    

    will print 1 if Foo(3,2) works. As noted, this is limited by failures of perfect forwarding.

    You can also pass Foo_overloads{} to functions expecting a function object, and it will dispatch at the call site instead of doing it when you pass the function object.

    0 讨论(0)
提交回复
热议问题