Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixing void_t and variadic templates?

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?

like image 299
Vincent Avatar asked Dec 19 '17 22:12

Vincent


2 Answers

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;
like image 106
Yakk - Adam Nevraumont Avatar answered Oct 21 '22 16:10

Yakk - Adam Nevraumont


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>{} );
 }
like image 1
max66 Avatar answered Oct 21 '22 17:10

max66