Ist es legal, einen Zeiger neu auf die Funktion zuzuweisen?

33

Zeiger auf Funktionen sind keine einfachen Datenzeiger, da sie nicht in einem void * -Zeiger gespeichert werden können. Es scheint jedoch, dass ich die Kopie eines Funktionszeigers im dynamischen Speicher (in gcc und clang) wie im folgenden Code speichern kann. Ist ein solcher Code gemäß C ++ Standard legal oder handelt es sich um eine Art Compiler-Erweiterung?

Darüber hinaus verhält sich der resultierende Zeiger auf den Funktionszeiger wie ein einfacher Datenzeiger: Ich kann ihn in void * speichern und durch static_cast aus void * abrufen. Ist dieses Verhalten durch den Standard garantiert?

int main()
{
  extern void fcn();
  void (*fcnPtr)() = &fcn;
  void (**ptrToFcnPtr)() = nullptr;

  //Make the copy of fcnPtr on the heap:
  ptrToFcnPtr = new decltype(fcnPtr)(fcnPtr);
  //Call the pointed-to function : 
  (**ptrToFcnPtr)();

  //Save the pointer in void* :
  void *ptr = ptrToFcnPtr;
  //retrieve the original ptr: 
  auto myPtr = static_cast< void(**)() > (ptr) ; 
  //free memory:
  delete ptrToFcnPtr ;

}
Adrian
quelle
2
Bitte verwenden Sie keine rohen Funktionszeiger. Verwenden Sie std::functionstattdessen.
Ein Programmierer
Sie brauchen das nicht newzu besetzen void*. void* ptr = &fcnPtr;funktioniert genauso gut, da fcnPtres sich um ein Objekt handelt, nicht um eine Funktion.
Walnuss
5
@Someprogrammerdude std::functionist ein typenlöschender Container zum Speichern eines beliebigen aufrufbaren, nicht wirklich ein Ersatz für Funktionszeiger…
Michael Kenzel
7
(@Someprogrammerdude) Bitte nicht blind verwenden / empfehlen std::function. Es ist großartig für seine Fähigkeit, "polymorphe" Funktionen zu speichern (dh alles mit der richtigen Signatur, auch wenn es den Status wie bei einigen Lambdas enthält), aber das erhöht auch den Overhead, der möglicherweise nicht benötigt wird. Ein Zeiger auf eine Funktion ist POD. A std::functionist nicht.
Matthew
2
@Matthew Um fair zu sein, fragt Adrian, ob er den Zeiger dynamisch der Funktion zuordnen und mit einer Typlöschung darauf zeigen soll. Im void*Kontext dieser Frage std::functionscheint es also genau das zu sein, wonach sie gesucht haben. Ich stimme zu, dass die generelle Ablehnung von Funktionszeigern durch die SPD nicht stichhaltig ist.
Eerorika

Antworten:

27

Während Funktionszeiger keine Objektzeiger sind, ist "Zeiger auf eine Funktion eines Typs" immer noch ein Objekttyp [basic.types] / 8 . Funktionszeiger sind also selbst Objekte, genau das, worauf sie zeigen, ist es nicht.

So können Sie sicher ein Objekt vom Typ Funktionszeiger über einen neuen Ausdruck erstellen…

Michael Kenzel
quelle
9

da sie (Funktionszeiger) nicht in einem void * -Zeiger gespeichert werden können.

Das Speichern eines Funktionszeigers als void*wird tatsächlich bedingt unterstützt. Dies bedeutet, dass es je nach Sprachimplementierung entweder gespeichert werden kann oder nicht. Wenn die Sprachimplementierung das dynamische Laden unterstützt, wird void*wahrscheinlich das Konvertieren des Funktionszeigers in unterstützt. GCC, Clang und MSVC unterstützen dies alle:

reinterpret_cast<void*>(&function);

Ist es legal, einen Zeiger neu auf die Funktion zuzuweisen?

Sicher. Alle Zeiger, einschließlich Funktionszeiger, sind Objekte und alle Objekte können dynamisch zugewiesen werden.

Darüber hinaus verhält sich der resultierende Zeiger auf den Funktionszeiger wie ein einfacher Datenzeiger

Funktionszeiger ist ein Objekt. Der Zeiger auf einen Funktionszeiger "verhält sich nicht nur als", sondern ist ein Zeiger auf ein Objekt.

Ich kann es in void * speichern und per static_cast aus void * abrufen. Ist dieses Verhalten durch den Standard garantiert?

Die Konvertierung zwischen Zeiger auf void und Zeiger auf Objekt ist zulässig, ja. Die Roundtrip-Konvertierung liefert garantiert den ursprünglichen Zeiger.

Eerorika
quelle
Vielen Dank. Aber um dann den Funktionszeiger in void * umzuwandeln (oder umgekehrt), muss ich reinterpret_cast verwenden, oder?
Adrian
1
@ Adrian Ja. Ich habe ein Beispiel hinzugefügt.
Eerorika
Wenn jemand wahrscheinlich eine Ausnahme machen möchte: Dos Medium Memory Model + Overlays. Funktionszeiger sind größer als Datenzeiger im mittleren Modell, und Überlagerungen können verwendet werden, um Plugins zu erzielen, wenn Sie sich genug anstrengen.
Joshua