Przekazywanie wskaźnika funkcji z tablicy wskaźników funkcji jako argumentu szablonu

9

Chciałbym przekazać wskaźnik funkcji z tablicy wskaźników funkcji jako argument szablonu. Mój kod wydaje się kompilować przy użyciu MSVC, chociaż Intellisense narzeka, że ​​coś jest nie tak. Zarówno gcc, jak i clang nie mogą skompilować kodu.

Rozważ następujący przykład:

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 kompiluje kod, ale Intellisense podaje następujący błąd:invalid nontype template argument of type "const FunctionPointer"

gcc nie może się skompilować z następującym komunikatem:

<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 nie kompiluje się z następującym komunikatem:

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

Pytania:

Jest wrapper_function<functions[0]>();ważny czy nie?

Jeśli tak nie jest, czy mogę coś przekazać functions[0]jako argument szablonu wrapper_function? Moim celem jest zbudowanie nowej tablicy wskaźników funkcji w czasie kompilacji z zawartością { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

Matti
źródło
Hmm, co ciekawe, myślałem, że problem polegał na tym, że używasz wartości (wskaźnika) zamiast typu. Ale nawet wrapper_function<decltype(functions[0])>()się nie kompiluje.
CoryKramer
6
Wydaje się, że działa w C ++ 17 ... teraz, aby znaleźć różnicę w standardzie ...
AndyG

Odpowiedzi:

5

Wyrażenie wrapper_function<functions[0]>();jest zabronione z powodu:

14.3.2 Argumenty nietypowe w szablonie [temp.arg.nontype]

Argumentem szablonu dla nietypowego parametru szablonu innego niż typ szablonu jest jeden z:

[...]

- wyrażenie stałe (5.19), które określa adres obiektu o przechowywaniu statycznym> czas trwania i powiązanie zewnętrzne lub wewnętrzne lub funkcję z powiązaniem zewnętrznym lub wewnętrznym, w tym szablony funkcji i identyfikatory szablonów funkcji, ale z wyłączeniem niestatycznych elementów klasy, wyrażone (ignorując nawiasy) jako wyrażenie & id, z wyjątkiem tego, że & może zostać pominięty, jeśli nazwa odnosi się do funkcji lub tablicy, i należy go pominąć, jeśli odpowiedni parametr szablonu jest odwołaniem; [...]

Zabronione jest używanie wskaźników jako nietypowych argumentów szablonów innych niż forma, &idwięc zasadniczo działałoby:

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
}

a następujący fragment kodu nie będzie działał po skompilowaniu z opcją C ++ 14:

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

Po skompilowaniu z opcją C ++ 17, twoje podejście i powyższe działałyby zarówno:

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

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

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

Zobacz na żywo

Orzechówka
źródło
Dozwolona jest nie tylko forma &id, ale także idfunkcje, jak pokazano w przykładzie, a wartość wskaźnika zerowego jest wyraźnie dozwolona w postaci wyrażenia ciągłego.
orzech
C ++ 17 zastępuje to „ konwertowanym stałym wyrażeniem ”, tzn. Pozwala na łączenie stałych wyrażeń, więc wrapper_function<func>()również będzie działać.
rustyx
Ok sprawdzi i zaktualizuje odpowiedź po napisaniu jej w całości. Tnx
NutCracker