Frage

kann ich hier völlig falsch sein, aber wie ich es verstehe, C ++ hat nicht wirklich einen native „Zeiger auf Member-Funktion“ Typen. Ich weiß, Sie Tricks mit Boost und mem_fun usw. tun können, aber warum haben die Designer von C ++ nicht entscheiden, ein 64-Bit-Zeiger hat einen Zeiger auf die Funktion und einen Zeiger auf das Objekt, zum Beispiel enthalten?

Was ich meine, speziell ein Zeiger auf eine Member-Funktion von a insbesondere Objekt von unbekannt Typ . I.E. etwas, das Sie für einen Rückruf verwenden . Dies wäre ein Typ sein, der enthält zwei Werte. Der erste Wert einen Zeiger auf die Funktion , und der zweite Wert ist ein Zeiger auf die spezifische Instanz des Objektes ist.

Was ich meine, nicht wissen, ist ein Zeiger auf eine allgemeine Elementfunktion einer Klasse. Z.

int (Fred::*)(char,float)

Es gewesen wäre, so nützlich und machte mir das Leben leichter.

Hugo

War es hilfreich?

Lösung

@RocketMagnet - Dies ist als Antwort auf Ihre ) ()" und "Leere (void :: ) ()" schließen sich gegenseitig inkompatible Typen.

Nun, ich wette, Sie denken „warten, ich Mitglied-Funktion-Zeiger geworfen haben, bevor ganz gut!“ Ja, Sie können haben, reinterpret_cast und einzelne Vererbung. Dies ist in der gleichen Kategorie von anderen reinterpret wirft:

#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};
class D { public: int z; };

int main() {
    C c; c.x = 42; c.y = -1;

    // this will print -1
    D& d = reinterpret_cast<D&>(c);
    cout << "d.z == " << d.z << "\n";
}

Also, wenn void (void::*)() nicht existierte, aber es gibt nichts, was man sicher könnte / portabel zuweisen.

Traditionell verwenden Sie Funktionen der Signatur void (*)(void*) irgendwo Sie möchten, was void (void::*)() zu verwenden, weil während Mitglied-Funktion-Zeiger werfen nicht gut auf und ab der Erbschaft Hierarchie, void-Zeiger und wirft. Statt dessen:

#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};

void do_foo(void* ptrToC){
    C* c = static_cast<C*>(ptrToC);
    c->foo();
}

int main() {
    typedef void (*pf_t)(void*);
    C c; c.x = 42; c.y = -1;

    pf_t f = do_foo;
    f(&c);
}

So zu Ihrer Frage. Warum nicht C ++ diese Art von Casting unterstützen. Pointer-to-Mitgliedsfunktionstypen haben bereits mit virtuellen beschäftigen vs nicht-virtuellen Basisklassen und virtuelle vs nicht-virtuelle Member-Funktionen, die alle in der gleichen Art, sie zu 4 * sizeof (void *) auf einigen Plattformen aufzublasen. Ich denke, weil es würde die Umsetzung der Zeiger-to-Mitglied-Funktion erschweren und rohe Funktionszeiger bereits dieses Problem lösen, so gut.

Wie andere haben gesagt, C ++ gibt Bibliothek Autoren genug Werkzeuge, um dies getan, und dann ‚normale‘ Programmierer wie du und ich, sollten diese Bibliotheken verwenden, anstatt diese Details des Schwitzens.

EDIT: markierte Community Wiki. Bitte nur bearbeiten relevante Verweise auf die C ++ Standard enthalten, und in kursiv hinzuzufügen. (Insb. Fügen Sie Verweise auf Standard, wo mein Verständnis falsch war! ^ _ ^)

Andere Tipps

Wie andere darauf hingewiesen, C ++ hat einen Funktionszeiger Typ Element.

Der Begriff Sie suchen ist „gebunden Funktion“. Der Grund, C ++ nicht Syntax Zucker-Bindung für Funktion bietet, ist wegen seiner Philosophie nur die grundlegendsten Werkzeuge zur Verfügung stellt, mit denen sie dann alles, was Sie wollen bauen. Dies hilft, die Sprache „kleine“ (oder zumindest weniger Geist bogglingly sehr groß).

Ebenso C ++ nicht über eine Sperre {} primitive wie C # 's, aber es hat RAII, die durch Auftrieb des scoped_lock verwendet wird.

Es gibt natürlich auch die Schule des Denkens, die sagt, Sie Syntax Zucker für alles sollte hinzufügen, die von Nutzen sein könnten. Für besser oder schlechter, ist C ++ nicht zu dieser Schule gehört.

Es tut.

Beispiel:

int (Fred::*)(char,float)

ist ein Zeiger auf eine Elementfunktion einer Klasse Fred, die eine int zurückgibt und eine char und float.

Ich denke, die Antwort ist, dass die Entwickler von C ++ wählen Sie nicht die Dinge in der Sprache zu haben, die in einer Bibliothek genauso einfach umgesetzt werden könnten. Ihre eigene Beschreibung von dem, was Sie wollen, gibt eine durchaus vernünftige Art und Weise, sie umzusetzen.

Ich weiß, es klingt komisch, aber C ++ ist eine minimalistische Sprache. Sie haben zu verlassen, um Bibliotheken alles, was sie sie verlassen können.

Die TR1 hat std :: tr1 :: function, und es wird zu C ++ 0x hinzugefügt werden. In einem gewissen Sinne es es hat.

Eines der Designphilosophie von C ++ ist: Sie zahlen nicht für das, was Sie nicht verwenden. Das Problem mit C # Stil ist sie Kongressteilnehmer sind schwer und Sprachunterstützung erfordern, dass jeder zahlen würde, ob sie benutzt oder nicht. Deshalb ist die Bibliothek Implementierung bevorzugt wird.

Der Grund Delegierten schwer sind, ist, dass ein Verfahren Zeiger ist oft größer als ein normaler Zeiger. Dies geschieht immer dann, wenn die Methode virtuell ist. Die Methode Zeiger ruft eine andere Funktion je nachdem, welche Basisklasse verwendet es. Das erfordert mindestens zwei Zeiger, die V-Tabelle und den Offset. Es gibt andere mit dem Verfahren beteiligt Seltsamkeit von einer Klasse in die Mehrfachvererbung beteiligt ist.

Alles, was gesagt, ich bin kein Compiler Schriftsteller. Es kann möglich eine neue Art für gebundene Methode Zeiger zu machen, die die virtuelle-ness der Methode verwiesen unterminieren würde (schließlich wissen wir, was die Basisklasse ist, wenn das Verfahren gebunden ist).

Die Frage ist sicherlich nicht die Grundlagen ein Objekt Zeiger und einen Funktionszeiger in einem einzigen, einfach zu bedienenden Paket, da Sie diese mit einem Zeiger und ein Thunk tun könnten. (Solche Thunks werden bereits von VC ++ auf x86 verwendet, um Zeiger auf virtuelle Elementfunktionen zu unterstützen, so dass diese Zeiger nur 4 Bytes in Anspruch nehmen.) Sie könnten mit vieler Thunks am Ende, es ist wahr, aber die Leute verlassen sich bereits auf dem Linker beseitigen doppelte Vorlage instantiations - ich jedenfalls - und es gibt nur so viele vTable und diese ausgleicht Sie mit in der Praxis am Ende werden. Der Aufwand wäre wahrscheinlich nicht für angemessen großes Programm von Bedeutung sein, und wenn man dieses Zeug nicht verwenden Sie dann wird es nicht kostet Sie nichts.

(Architekturen, die traditionell einen TOC verwenden würde speichern Sie den TOC-Zeiger in dem Funktionszeigerteil, anstatt in dem Thunk, wie sie bereits zu tun haben würden.)

(Diese neue Art von Objekt nicht genau sein substituierbar ein normaler Zeiger auf Funktion, natürlich, weil die Größe anders sein würde. Sie würden jedoch beim Aufruf Punkt das gleiche geschrieben werden.)

Das Problem, das ich sehe, ist, dass der Aufrufkonvention: Unterstützung Zeiger auf Funktionen könnten auf diese Weise im allgemeinen Fall schwierig sein, da der generierte Code die Argumente vorbereiten würde (diese enthalten) auf die gleiche Weise ohne Rücksicht auf die tatsächliche Art der Sache, Funktion oder Member-Funktion, auf die der Zeiger zeigt.

Dies ist wahrscheinlich kein großes Problem auf x86, zumindest nicht mit Thiscall, weil Sie gerade ECX laden konnten, unabhängig und akzeptieren, dass, wenn die anrufende Funktion nicht, dann braucht es gefälschte sein wird. (Und ich denke, VC ++ übernimmt ECX ohnehin in diesem Fall falsche ist.) Aber auf Architekturen, die Argumente für benannte Parameter in Registern Funktionen übergeben, können Sie in dem Thunk mit einem fairen Betrag von schlurfenden, und wenn Stapel Argumente links am Ende geschoben -zu-rechts, dann sind Sie im Grunde gestopft. Und dies kann nicht statisch festgelegt werden, weil in der Grenze gibt es keine Quer Übersetzung Einheit-Information.

[Edit: MSalters, in einem Kommentar zu rocketmagnet des Beitrag oben, weist darauf hin, dass, wenn beiden Objekte und Funktion bekannt sind, dann wird den dieser Offset und so weiter kann sofort bestimmt werden. Dieser total hat mir nicht ein! Aber mit diesem Hintergrund, nehme ich sich braucht nur die genauen Objektzeiger gespeichert werden, vielleicht versetzt, und die genauen Funktionszeiger. Dies macht Thunks völlig unnötig - ich glaube -. Aber ich bin mir ziemlich sicher, dass die Fragen des Zeigens auf Elementfunktionen und Nicht-Mitgliederfunktionen gleich bleiben würden]

C ++ ist bereits eine große Sprache, und das Hinzufügen es größer gemacht hätte. Was Sie wirklich wollen, ist noch schlimmer als nur eine gebundene Memberfunktion, es ist etwas näher an boost :: Funktion. Sie möchten sich für Rückrufe sowohl eine void(*)() und ein Paar speichern. Schließlich ist der Grund, dass Sie die dem Anrufer einen vollständigen Rückruf geben mögen, und die Angerufene über die genauen Details nicht kümmern sollte.

Die Größe wahrscheinlich sizeof(void*)+sizeof(void(*)()) würde. Zeiger auf Elementfunktionen können größer sein, aber das ist, weil sie ungebunden sind. Sie müssen mit der Möglichkeit befassen, die youre die Adresse einer virtuellen Funktion‘nehmen, zum Beispiel. Jedoch ist ein Einbau-gebundenen-pointer-to-Mitglied-Funktion Typ würde nicht von diesem Kopf leidet. Es kann die genaue Funktion löst im Moment der Bindung genannt werden.

Dies ist mit einem UDT nicht möglich. boost :: Funktion nicht den Overhead eines PTMF verwerfen kann, wenn er das Objekt Zeiger bindet. Sie müssen wissen, verstehen Struktur eines PTMF, VTable, etcetera - alle Nicht-Standard-Sachen. Allerdings könnten wir es jetzt mit C ++ 1x erhalten. Sobald es in std :: Neues, es ist fair game für Compiler-Anbieter. Die Standardbibliothek Implementierung selbst ist nicht tragbar (siehe zum Beispiel type_info).

Sie würden noch einige nette Syntax haben wollen, glaube ich, auch wenn ein Compiler-Anbieter implementiert diese in der Bibliothek. Ich möchte std::function<void(*)()> foo = &myX && X::bar. (Es ist nicht mit bestehenden Syntax kollidiert, wie X :: bar kein Ausdruck ist - nur & X :: bar ist)

Es kommt zu mir, dass Methoden ein implizites this Argument haben, so ein c Zeiger auf ein Verfahren unzureichend ist das Verfahren zu ermöglichen, genannt zu werden (weil es keine Möglichkeit, welche Instanz zu bestimmen ist, sollte für this verwendet werden (oder auch wenn jeder Instanz ist derzeit noch vorhandene)).

Edit: Rocketmagnet kommentiert, dass er dies in der Frage angesprochen, und das scheint der Fall zu sein, obwohl ich glaube, dass hinzugefügt wurde, nachdem ich diese Antwort gestartet. aber ich werde sagen: "mea culpa", sowieso.

So lassen Sie mich auf den Gedanken ein wenig erweitern.

C ++ eng mit c verbunden ist, und hat alle seine Eigenart kompatibel mit der älteren Sprache (vor allem wegen der Geschichte der c++ Entwicklung, nehme ich an). Also, ein intrinsischer c++ Zeiger ist ein c Zeiger, und ist nicht in der Lage der Unterstützung die Verwendung Sie verlangen.

Sicherlich könnte man einen abgeleiteten Typ bauen die Arbeit zu tun --- wie in der Boost-Implementierung --- aber ein solches Lebewesen gehört in einer Bibliothek.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top