6

Consider the following code:

template <class F, class... Args, class = std::void_t<>>
struct is_invokable
: std::false_type {};
template <class F, class... Args>
struct is_invokable<F, Args..., std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type {};

The goal is to have a trait that is able to tell whether a callable of type F is invokable with arguments of type Args....

However, it fails to compile because:

error: parameter pack 'Args' must be at the end of the template parameter list

What is the (elegant) way to do this in C++17?

Jodocus
  • 7,493
  • 1
  • 29
  • 45
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 4
    Well, where else other than the end can you put `void`? Put it there. – Barry Dec 19 '17 at 22:32
  • Huh. I didn’t realize that types required the pack to be in the final position. Functions don’t, if you give default arguments or they can be deduced. – Daniel H Dec 19 '17 at 22:46

2 Answers2

7
namespace details {
  template <class F, class, class... Args>
  struct is_invokable : std::false_type {};
  template <class F, class... Args>
  struct is_invokable<F, std::void_t<std::invoke_result_t<F, Args...>>, Args...>
  : std::true_type {};
}
template <class F, class... Args>
using is_invokable=typename ::details::is_invokable<F, void, Args...>::type;
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

I propose an helper struct (is_invokable_h) and the use of std::tuple to wrap the Args...

Something like

#include <type_traits>
#include <utility>

template <typename, typename, typename = void>
struct is_invokable_h : std::false_type
 {};

template <typename F, typename ... Args>
struct is_invokable_h<F, std::tuple<Args...>,
                      std::void_t<std::invoke_result_t<F, Args...>>>
   : std::true_type
 {};

template <typename F, typename ... Args>
struct is_invokable : is_invokable_h<F, std::tuple<Args...>>
 {};

int foo (int)
 { return 0; }

int main()
 {
   static_assert( true  == is_invokable<decltype(foo), int>{} );
   static_assert( false == is_invokable<decltype(foo), int, int>{} );
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • 2
    Or [std::is_invocable](http://en.cppreference.com/w/cpp/types/is_invocable) introduced by C++17. – O'Neil Dec 19 '17 at 22:57
  • You're right: or `std::is_invocable`. But was my intention show a possible way to bypass the problem of the default template type after a template variadic list. – max66 Dec 19 '17 at 23:14