3

I am trying to deduce callback function parameters from a callback given as a template parameter. I have found this previous answer: Can you extract types from template parameter function signature which looks like it'd be the answer, but somehow I cannot get this to work for what I want.

I have the following code:

#include <tuple>

template<typename S>
struct signature;

template<typename R, typename... Args>
struct signature<R(Args...)>
{
    using return_type = R;
    using argument_type = std::tuple<Args...>;
};

template <auto callback>
void check_callback()
{
    using callback_type = decltype(callback);
    using arg1          = std::tuple_element_t<0, signature<callback_type>::argument_type>;
}

int main()
{
}

When compiling this with clang 6.0 as c++17 I get the following error:

error: template argument for template type parameter must be a type; did you forget 'typename'?

If I change the line defining callback_type to something like

using callback_type = void(int);

then it suddenly works, so given a valid function signature the code seems to work. Why won't it work when using decltype on a function coming from a template parameter?

Martijn Otto
  • 878
  • 4
  • 21

2 Answers2

5

Why won't it work when using decltype on a function coming from a template parameter?

Because in this case callback_type is a dependent name, which depends on the template parameter callback. Then you have to use the keyword typename.

Inside a declaration or a definition of a template, typename can be used to declare that a dependent name is a type.

So change the code to

using arg1 = std::tuple_element_t<0, typename signature<callback_type>::argument_type>;
//                                   ^^^^^^^^

using callback_type = void(int); doesn't have such issue; callback_type is an independent name.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
3

The full error message is (clang 9.0.0)

prog.cc:17:51: error: template argument for template type parameter must be a type; did you forget 'typename'?
    using arg1          = std::tuple_element_t<0, signature<callback_type>::argument_type>;
                                                  ^
                                                  typename

It is not the line above that is problematic, but you need to assure your compiler that signature<callback_type>::argument_type is a type, because that depends on the template parameter (via decltype(callback)). So you have to add typename:

using arg1          = std::tuple_element_t<0, typename signature<callback_type>::argument_type>;
                                           //   ^^

In case of using callback_t = void(int); there is no such ambiguity, because in that case signature<callback_type> does not depend on the template parameter callback and the compiler already knows that signature<callback_type>::argument_type is a type, not a value.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185