1

Given a template function of the signature

template <typename F>
void fn(F f);

I would like to extract the parameter pack of the given function signature F, if a callable function was given to fn().

Unlike in foo0() I am not able to do this in foo1() and foo2() in the following example (simplified to a single parameter instead of a parameter pack):

#include <type_traits>

template <typename Sig> struct argument_of;
template <typename R, typename Arg> struct argument_of<R(Arg)> { typedef Arg type; };

void bar(int);

void foo0() {
    static_assert(std::is_same<int, typename argument_of<decltype(bar)>::type>::value, "!");
}

template <typename F>
void foo1(F) {
    static_assert(std::is_same<int, typename argument_of<F>::type>::value, "!");
}

template <typename F>
void foo2(F f) {
    static_assert(std::is_same<int, typename argument_of<decltype(f)>::type>::value, "!");
}

int main() {
    foo0();
    foo1(bar);
    foo2(bar);
}

See live example.

What would be the proper way if the calling style of fn() remains as given for foo1() and foo2() in the example above?

user2525536
  • 366
  • 1
  • 4
  • 14

1 Answers1

2

You just need a specialization to handle a function pointer type as well:

template <typename Sig> struct argument_of;
template <typename R, typename Arg> struct argument_of<R(Arg)> { typedef Arg type; };
template <typename R, typename Arg> struct argument_of<R(*)(Arg)> { typedef Arg type; };

Its a small distinction, and I hope that I get my terminology right, but the symbol bar is a non-overloaded function of type void(int) whereas the variable f (or unnamed F-parameter), is a function pointer of type void(*)(int). Variables and functions are fundamentally different in C++ and the type-system has this distinction.

Its also worth noting that another variant that you may wish to specialize for is a function reference which would look like void(&)(int). A function reference type can be deduced in situations like decltype(*f) or decltype((bar)) (note the extra parenthesis).

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • There's also the accepted answer [here](https://stackoverflow.com/questions/34437557/difference-between-voidint-void-int) that briefly talks about the differences in terms of value categories and is worth a read. – kmdreko Mar 02 '19 at 06:55
  • 1
    Good hint. So I could have done it [this](http://cpp.sh/9kruno) way as well, using `std::remove_pointer` on `F` or `decltype(f)` before resolving it with `argument_of`. – user2525536 Mar 02 '19 at 07:39