Search code examples
c++templatesfunction-pointersvariadic-templatestemplate-argument-deduction

"no matching function for call to" when having a function pointer with template arguments as a template argument


I'm writing a template wrapper function that can be applied to a functions with different number/types of arguments. I have some code that works but I'm trying to change more arguments into template parameters.

The working code:

#include <iostream>

int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...

template<typename ...ARGS>
int wrapper(int (*func)(ARGS...), ARGS... args) { return (*func)(args...) * 10; }

int wrappedFunc0(bool b) { return wrapper<bool>(func0, b); }

int main()
{
    std::cout << wrappedFunc0(true) << std::endl;
    return 0;
}

Now I want int (*func)(ARGS...) to also be a template parameter. (It's for performance reasons. I want the pointer to be backed into the wrapper, because the way I'm using it prevents the compiler from optimizing it out.)

Here is what I came up with (The only difference is I've changed the one argument into a template parameter.):

#include <iostream>

int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...

template<typename ...ARGS, int (*FUNC)(ARGS...)>
int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }

int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }

int main()
{
    std::cout << wrappedFunc0(true) << std::endl;
    return 0;
}

This doesn't compile. It shows:

<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
    9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
      |                                   ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
    7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
      |     ^~~~~~~
<source>:7:5: note:   template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
    9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
      |                                   ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note:   expected a type, got 'func0'
ASM generation compiler returned: 1
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
    9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
      |                                   ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
    7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
      |     ^~~~~~~
<source>:7:5: note:   template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
    9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
      |                                   ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note:   expected a type, got 'func0'
Execution build compiler returned: 1

(link to the compiler explorer)

It looks like a problem with the compiler to me, but GCC and Clang agree on it so maybe it isn't.

Anyway, how can I make this template compile correctly with templated pointer to a function?

EDIT: Addressing the duplicate flag Compilation issue with instantiating function template I think the core of the problem in that question is the same as in mine, however, it lacks a solution that allows passing the pointer to function (not only its type) as a template parameter.


Solution

  • This doesn't work because a pack parameter (the one including ...) consumes all remaining arguments. All arguments following it can't be specified explicitly and must be deduced.

    Normally you write such wrappers like this:

    template <typename F, typename ...P>
    int wrapper(F &&func, P &&... params)
    {
        return std::forward<F>(func)(std::forward<P>(params)...) * 10;
    }
    

    (And if the function is called more than once inside of the wrapper, all calls except the last can't use std::forward.)

    This will pass the function by reference, which should be exactly the same as using a function pointer, but I have no reasons to believe that it would stop the compiler from optimizing it.

    You can force the function to be encoded in the template argument by passing std::integral_constant<decltype(&func0), func0>{} instead of func0, but again, I don't think it's going to change anything.