Передача указателя на функцию из массива указателей на функцию в качестве аргумента шаблона

9

Я хотел бы передать указатель на функцию из массива указателей на функцию в качестве аргумента шаблона. Кажется, мой код компилируется с использованием MSVC, хотя Intellisense жалуется, что что-то не так. И gcc, и clang не могут скомпилировать код.

Рассмотрим следующий пример:

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 компилирует код, но Intellisense выдает следующую ошибку:invalid nontype template argument of type "const FunctionPointer"

gcc не может скомпилировать следующее сообщение:

<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 не может скомпилировать следующее сообщение:

<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.

Вопросов:

Является wrapper_function<functions[0]>();действительным или нет?

Если это не так, могу ли я что-то сделать functions[0]в качестве аргумента шаблона wrapper_function? Моя цель - создать новый массив указателей на функции во время компиляции с содержимым { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

Матти
источник
Хм, это интересно, я думал, что проблема была в том, что вы использовали значение (указатель) вместо типа. Но даже wrapper_function<decltype(functions[0])>()не компилируется.
CoryKramer
6
Кажется, работает в C ++ 17 ... сейчас, чтобы найти разницу в стандарте ...
AndyG

Ответы:

5

Выражение wrapper_function<functions[0]>();запрещено из-за следующего:

14.3.2 Шаблонные нетиповые аргументы [temp.arg.nontype]

Шаблонный аргумент для нетипового, нешаблонного шаблона-параметра должен быть одним из:

[...]

- константное выражение (5.19), которое обозначает адрес объекта со статическим хранилищем> длительностью и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженные (игнорируя скобки) как & id-выражение, за исключением того, что & может быть опущено, если имя относится к функции или массиву, и должно быть опущено, если соответствующий параметр шаблона является ссылкой; [...]

Запрещается использовать указатели в качестве аргументов шаблонного типа, отличного от формы, &idпоэтому, в основном, будет работать следующее:

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
}

и следующий фрагмент не будет работать при компиляции с опцией C ++ 14:

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

При компиляции с опцией C ++ 17 ваш подход и предыдущий будут работать:

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

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

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

Смотреть в прямом эфире

Щелкунчик
источник
Как показано в примере, для функций разрешена не только форма &id, но и idфункция, а значение нулевого указателя явно разрешено в форме константного выражения.
грецкий орех
C ++ 17 заменяет это « преобразованным константным выражением », то есть позволяет связывать константные выражения, поэтому wrapper_function<func>()также будет работать.
rustyx
Хорошо проверим и обновлю ответ после того, как напишу его целиком. Tnx
NutCracker