Pasar un puntero de función desde una matriz de punteros de función como argumento de plantilla

9

Me gustaría pasar un puntero de función de una matriz de punteros de función como argumento de plantilla. Mi código parece compilarse usando MSVC aunque Intellisense se queja de que algo está mal. Tanto gcc como clang no pueden compilar el código.

Considere el siguiente ejemplo:

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 el código pero Intellisense da el siguiente error:invalid nontype template argument of type "const FunctionPointer"

gcc falla al compilar con el siguiente mensaje:

<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 no se compila con el siguiente mensaje:

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

Preguntas:

¿Es wrapper_function<functions[0]>();válido o no?

Si no es así, ¿hay algo que pueda hacer para pasar functions[0]como argumento de plantilla wrapper_function? Mi objetivo es construir una nueva matriz de punteros de función en tiempo de compilación, con el contenido { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

Matti
fuente
Hmm eso es interesante, pensé que el problema era que estabas usando un valor (un puntero) en lugar de un tipo. Pero incluso wrapper_function<decltype(functions[0])>()no compila.
CoryKramer
66
Parece funcionar en C ++ 17 ... ahora para encontrar la diferencia en standardese ...
AndyG

Respuestas:

5

La expresión wrapper_function<functions[0]>();está prohibida por lo siguiente:

14.3.2 Argumentos sin tipo de plantilla [temp.arg.nontype]

Un argumento de plantilla para un parámetro de plantilla que no sea de tipo ni plantilla será uno de:

[...]

- una expresión constante (5.19) que designa la dirección de un objeto con almacenamiento estático> duración y vinculación externa o interna o una función con vinculación externa o interna, incluidas las plantillas de función y los identificadores de plantilla de función, pero excluyendo los miembros de clase no estáticos, expresados (ignorando paréntesis) como & id-expresión, excepto que & puede omitirse si el nombre se refiere a una función o matriz y se omitirá si el parámetro de plantilla correspondiente es una referencia; [...]

Está prohibido usar punteros como argumentos de plantilla que no sean de tipo distinto del formulario &id, por lo que, básicamente, lo siguiente funcionaría:

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
}

y el siguiente fragmento no funcionará cuando se compila con la opción C ++ 14:

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

Cuando se compila con la opción C ++ 17, su enfoque y el anterior funcionarían:

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 en vivo

Cascanueces
fuente
No solo la forma &id, sino que también idestá permitida para funciones, como está demostrando en el ejemplo y un valor de puntero nulo está explícitamente permitido en forma de expresión constante.
nogal
C ++ 17 reemplaza eso con una " expresión constante convertida ", es decir, permite encadenar expresiones constantes, por wrapper_function<func>()lo que también funcionará.
rustyx
Ok verificará y actualizará la respuesta después de escribirla en su totalidad. Tnx
NutCracker