10

I would like to pass a function pointer from an array of function pointers as a template argument. My code seems to compile using MSVC even though Intellisense complains that something is wrong. Both gcc and clang fail to compile the code.

Consider the following example:

static void test() {}

using FunctionPointer = void(*)();

static constexpr FunctionPointer functions[] = { test };

template <FunctionPointer function>
static void wrapper_function()
{
    function();
}

int main()
{
    test();  // OK
    functions[0]();  // OK

    wrapper_function<test>();  // OK
    wrapper_function<functions[0]>();  // Error?
}

MSVC compiles the code but Intellisense gives the following error: invalid nontype template argument of type "const FunctionPointer"

gcc fails to compile with the following message:

<source>: In function 'int main()':
<source>:19:33: error: no matching function for call to 'wrapper_function<functions[0]>()'
   19 |  wrapper_function<functions[0]>();  // Error?
      |                                 ^
<source>:8:13: note: candidate: 'template<void (* function)()> void wrapper_function()'
    8 | static void wrapper_function()
      |             ^~~~~~~~~~~~~~~~
<source>:8:13: note:   template argument deduction/substitution failed:
<source>:19:30: error: '(FunctionPointer)functions[0]' is not a valid template argument for type 'void (*)()'
   19 |  wrapper_function<functions[0]>();  // Error?
      |                   ~~~~~~~~~~~^
<source>:19:30: note: it must be the address of a function with external linkage

clang fails to compile with the following message:

<source>:19:2: error: no matching function for call to 'wrapper_function'
        wrapper_function<functions[0]>();  // Error?
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:8:13: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'function'
static void wrapper_function()
            ^
1 error generated.

Questions:

Is wrapper_function<functions[0]>(); valid or not?

If it isn't, is there anything I can do to pass functions[0] as a template argument to wrapper_function? My goal is to construct a new array of function pointers at compile time, with the content { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

2
  • Hmm that is interesting, I thought the issue was that you were using a value (a pointer) instead of a type. But even wrapper_function<decltype(functions[0])>() doesn't compile. Commented Jan 17, 2020 at 13:24
  • 6
    Seems to work in C++17... now to find the difference in standardese... Commented Jan 17, 2020 at 13:25

1 Answer 1

5

Expression wrapper_function<functions[0]>(); is forbidden because of the following:

14.3.2 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

[...]

— a constant expression (5.19) that designates the address of an object with static storage > duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; [...]

It is forbidden to use pointers as non-type template arguments other than of the form &id so, basically, following would work:

static void test() {}

using FunctionPointer = void(*)();

static constexpr FunctionPointer functions[] = { test };

template <FunctionPointer function>
static void wrapper_function()
{
    function();
}

int main()
{
    test();  // OK
    functions[0]();  // OK

    wrapper_function<test>();  // OK
    wrapper_function<&test>();  // OK
}

and following snippet won't work when compiled with C++14 option:

constexpr auto func = &test;
wrapper_function<func>();

When compiled with C++17 option, your approach and the one above would both work:

int main()
{
    test();  // OK
    functions[0]();  // OK

    wrapper_function<test>();  // OK
    wrapper_function<&test>();  // OK
    wrapper_function<func>();  // OK

    wrapper_function<functions[0]>();  // OK
}

See live

Sign up to request clarification or add additional context in comments.

3 Comments

Not only the form &id, but also id is allowed for functions, as you are demonstrating in the example and a null pointer value is explicitly allowed in constant expression form.
C++17 replaces that with a "converted constant expression", i.e. it allows to chain constant expressions so wrapper_function<func>() will also work.
Ok will check and update the answer after I write it in whole. Tnx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.