Passando um ponteiro de função de uma matriz de ponteiros de função como um argumento de modelo

9

Gostaria de passar um ponteiro de função de uma matriz de ponteiros de função como um argumento de modelo. Meu código parece compilar usando o MSVC, apesar do Intellisense reclamar que algo está errado. O gcc e o clang falham ao compilar o código.

Considere o seguinte exemplo:

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 compila o código, mas o Intellisense fornece o seguinte erro:invalid nontype template argument of type "const FunctionPointer"

O gcc falha ao compilar com a seguinte mensagem:

<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

O clang falha ao compilar com a seguinte mensagem:

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

Questões:

É wrapper_function<functions[0]>();válido ou não?

Se não for, há algo que eu possa fazer para passar functions[0]como argumento de modelo wrapper_function? Meu objetivo é construir uma nova matriz de ponteiros de função em tempo de compilação, com o conteúdo { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

Matti
fonte
Hum, isso é interessante, pensei que o problema era que você estava usando um valor (um ponteiro) em vez de um tipo. Mas nem wrapper_function<decltype(functions[0])>()compila.
CoryKramer 17/01
6
Parece trabalhar em C ++ 17 ... agora para encontrar a diferença em padrões ...
AndyG 17/01

Respostas:

5

A expressão wrapper_function<functions[0]>();é proibida devido ao seguinte:

14.3.2 Argumentos sem tipo de modelo [temp.arg.nontype]

Um argumento-modelo para um parâmetro-modelo não-modelo e não-modelo deve ser um dos seguintes:

[...]

- uma expressão constante (5.19) que designa o endereço de um objeto com armazenamento estático> duração e ligação externa ou interna ou uma função com ligação externa ou interna, incluindo modelos de função e IDs de modelo de função, mas excluindo membros da classe não estáticos, expressos (ignorando parênteses) como & id-expression, exceto que & pode ser omitido se o nome se referir a uma função ou matriz e deve ser omitido se o parâmetro-modelo correspondente for uma referência; [...]

É proibido usar ponteiros como argumentos de modelo que não sejam do tipo, exceto no formulário &id, portanto, basicamente, o seguinte funcionaria:

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
}

e o seguinte snippet não funcionará quando compilado com a opção C ++ 14:

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

Quando compilada com a opção C ++ 17, sua abordagem e a acima funcionariam:

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

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

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

Ver ao vivo

NutCracker
fonte
Não apenas o formulário &id, mas também idé permitido para funções, como você está demonstrando no exemplo, e um valor de ponteiro nulo é explicitamente permitido no formulário de expressão constante.
walnut
O C ++ 17 substitui isso por uma " expressão constante convertida ", ou seja, permite encadear expressões constantes para wrapper_function<func>()que também funcione.
rustyx 17/01
Ok verificará e atualizará a resposta depois que eu a escrever na íntegra. Tnx
NutCracker 17/01