Übergeben eines Funktionszeigers aus einem Array von Funktionszeigern als Vorlagenargument

9

Ich möchte einen Funktionszeiger aus einem Array von Funktionszeigern als Vorlagenargument übergeben. Mein Code scheint mit MSVC kompiliert zu werden, obwohl Intellisense sich darüber beschwert, dass etwas nicht stimmt. Sowohl gcc als auch clang können den Code nicht kompilieren.

Betrachten Sie das folgende Beispiel:

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 kompiliert den Code, aber Intellisense gibt den folgenden Fehler aus:invalid nontype template argument of type "const FunctionPointer"

gcc kann nicht mit der folgenden Meldung kompiliert werden:

<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 kann nicht mit der folgenden Meldung kompiliert werden:

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

Fragen:

Ist wrapper_function<functions[0]>();gültig oder nicht?

Wenn nicht, kann ich irgendetwas tun, um es functions[0]als Vorlagenargument zu übergeben wrapper_function? Mein Ziel ist es, beim Kompilieren ein neues Array von Funktionszeigern mit dem Inhalt zu erstellen { wrapper_function<functions[0]>, ..., wrapper_function<functions[std::size(functions) - 1]> }.

Matti
quelle
Hmm das ist interessant, ich dachte das Problem war, dass Sie einen Wert (einen Zeiger) anstelle eines Typs verwenden. Aber auch wrapper_function<decltype(functions[0])>()nicht kompiliert.
CoryKramer
6
Scheint in C ++ 17 zu funktionieren ... jetzt, um den Unterschied in Standard zu finden ...
AndyG

Antworten:

5

Der Ausdruck wrapper_function<functions[0]>();ist aus folgenden Gründen verboten:

14.3.2 Nicht typisierte Vorlagenargumente [temp.arg.nontype]

Ein Template-Argument für einen Nicht-Typ-Nicht-Template-Template-Parameter muss eines der folgenden sein:

[...]

- ein konstanter Ausdruck (5.19), der die Adresse eines Objekts mit statischer Speicherdauer> Dauer und externer oder interner Verknüpfung oder einer Funktion mit externer oder interner Verknüpfung bezeichnet, einschließlich Funktionsvorlagen und Funktionsvorlagen-IDs, jedoch ohne nicht statische Klassenmitglieder, ausgedrückt (Klammern ignorieren) als & id-Ausdruck, außer dass das & weggelassen werden kann, wenn sich der Name auf eine Funktion oder ein Array bezieht, und weggelassen werden soll, wenn der entsprechende Template-Parameter eine Referenz ist; [...]

Es ist verboten, Zeiger als andere Nicht-Typ-Vorlagenargumente als das Formular zu verwenden, &idsodass im Grunde Folgendes funktionieren würde:

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
}

und das folgende Snippet funktioniert nicht, wenn es mit der Option C ++ 14 kompiliert wird:

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

Wenn Sie mit der Option C ++ 17 kompiliert werden, funktionieren sowohl Ihr als auch der oben beschriebene Ansatz:

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

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

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

siehe Live

Nussknacker
quelle
Wie Sie im Beispiel demonstrieren &id, idist nicht nur das Formular , sondern auch Funktionen zulässig, und ein Nullzeigerwert ist in Form eines konstanten Ausdrucks explizit zulässig.
Walnuss
C ++ 17 ersetzt dies durch einen " konvertierten konstanten Ausdruck ", dh es ermöglicht das Verketten konstanter Ausdrücke, sodass dies wrapper_function<func>()auch funktioniert.
Rustyx
Ok wird die Antwort überprüfen und aktualisieren, nachdem ich sie vollständig geschrieben habe. Tnx
NutCracker