In C ++ fehlt das Äquivalent zum PHP- self
Schlüsselwort , das den Typ der einschließenden Klasse ergibt.
Es ist einfach genug, es pro Klasse zu fälschen:
struct Foo
{
typedef Foo self;
};
aber ich musste Foo
nochmal schreiben . Vielleicht verstehe ich das eines Tages falsch und verursache einen stillen Fehler.
Kann ich eine Kombination von decltype
und Freunden verwenden, um diese Arbeit "autonom" zu machen? Ich habe bereits Folgendes versucht , this
ist aber an dieser Stelle nicht gültig:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Ich werde mir keine Sorgen um das Äquivalent von machen static
, was dasselbe tut, aber mit später Bindung.)
this_t
wäre wahrscheinlich eher auf die reguläre C ++ - Benennung ausgerichtet.auto()
und~auto()
für ctors / dtors. Interessant, um es gelinde auszudrücken. Wenn für diesen Zweck vielleicht verwendettypedef auto self;
, aber das scheint mir ein bisschen skizzenhaft.decltype(class)
, vielleicht mit einemdecltype(struct)
Äquivalent. Das ist viel klarer als nurauto
in einem bestimmten Kontext und ich sehe keine Probleme damit, dass es in die Sprache passt, auf der es basiertdecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
Funktioniert nicht mit Klassenvorlagen ...Antworten:
So können Sie es tun, ohne die Art von Foo zu wiederholen:
Wenn Sie ableiten möchten
Foo
sollten Sie das MakroWITH_SELF_DERIVED
folgendermaßen verwenden:Sie können sogar mehrere Vererbungen mit so vielen Basisklassen durchführen, wie Sie möchten (dank variadischer Vorlagen und variadischer Makros):
Ich habe dies überprüft, um auf gcc 4.8 und clang 3.4 zu funktionieren.
quelle
auto
unddecltype
oder in diesem Fall vonself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
wäre einfacher gewesen und würde eine genauere Kontrolle über die Vererbung ermöglichen - irgendwelche Gründe dagegen?Eine mögliche Problemumgehung (da Sie den Typ noch einmal schreiben müssen):
Für eine sicherere Version könnten wir sicherstellen, dass sich
T
Folgendes ergibtSelf<T>
:Beachten Sie, dass eine
static_assert
Funktion innerhalb eines Mitglieds wahrscheinlich die einzige Möglichkeit ist, dies zu überprüfen, da übergebene Typenstd::is_base_of
vollständig sein müssen.quelle
typename
das typedef. Und da dies die Anzahl der Redundanzen nicht verringert, halte ich es nicht für eine praktikable Alternative.Foo
Namen zu wiederholen .Foo
, müssen Sie entweder: (1) das T nach oben zum Blattnachkommen verbreiten oder (2) daran denken, viele Male von SelfT zu erben , oder (3) akzeptieren, dass alle Kinder etwas sind, um die Basis zu sein .. brauchbar, aber unpretty.self
eher versuche zu replizieren alsstatic
, ist das kein Problem.Sie können anstelle einer regulären Klassendeklaration ein Makro verwenden, das dies für Sie erledigt.
Und dann wie verwenden
#define END_CLASS };
würde wahrscheinlich die Lesbarkeit verbessern.Sie können auch @ Paranaix's nehmen
Self
und es verwenden (es wird langsam richtig hackisch)quelle
{
, so dass die}
„hängende“ ist, die Texteditoren wahrscheinlich, würden auch nicht mögen.CLASS_WITH_SELF(foo) { … };
- und ich denke, das ist unmöglich zu erreichen.Self
hat.Ich habe keine positiven Beweise, aber ich denke, es ist unmöglich. Folgendes schlägt fehl - aus dem gleichen Grund wie Ihr Versuch - und ich denke, das ist das weiteste, was wir bekommen können:
Dies zeigt im Wesentlichen, dass der Bereich, in dem wir unser typedef deklarieren möchten, einfach keinen Zugriff (sei es direkt oder indirekt)
this
hat und es keine andere (vom Compiler unabhängige) Möglichkeit gibt, zum Typ oder Namen der Klasse zu gelangen.quelle
decltype
ist ein nicht bewerteter Kontext, so dass das Aufrufen der Mitgliedsfunktion nicht das Problem ist (das nicht versucht wird)struct S { int i; typedef decltype(i) Int; };
funktioniert, obwohli
es sich um ein nicht statisches Datenelement handelt. Es funktioniert, weildecltype
es eine spezielle Ausnahme gibt, bei der ein einfacher Name nicht als Ausdruck ausgewertet wird. Aber ich kann mir keine Möglichkeit vorstellen, diese Möglichkeit so zu nutzen, dass die Frage beantwortet wird.Sowohl in GCC als auch in clang funktioniert das Erstellen eines Typedef, auf das verwiesen wird,
this
indemthis
im Trailing-Return-Typ einer Funktion typedef verwendet wird. Da dies nicht die Deklaration einer statischen Elementfunktion ist, wird die Verwendung vonthis
toleriert. Sie können dieses typedef dann zum Definieren verwendenself
.Leider besagt eine strikte Lektüre des Standards, dass auch dies nicht gültig ist. Clang überprüft, ob
this
es bei der Definition einer statischen Elementfunktion nicht verwendet wird. Und hier ist es tatsächlich nicht. GCC macht es nichts aus, wennthis
es in einem Trailing-Return-Typ verwendet wird, unabhängig von der Art der Funktion, es erlaubt es sogar fürstatic
Mitgliedsfunktionen. Was der Standard jedoch tatsächlich verlangt, ist dasthis
nicht außerhalb der Definition einer nicht statischen Elementfunktion (oder eines nicht statischen Datenelementinitialisierers) verwendet wird. Intel macht es richtig und lehnt dies ab.Vorausgesetzt, dass:
this
ist nur in nicht statischen Datenelementinitialisierern und nicht statischen Elementfunktionen zulässig ([expr.prim.general] p5),this
können auch in nicht bewerteten Kontexten nur mit unqualifiziertem Namen aufgerufen werden, wenn sie verwendet werden können ([over.call.func] p3).Ich denke, ich kann abschließend sagen, dass es überhaupt keine Möglichkeit gibt
self
, etwas zu implementieren, ohne irgendwo den Typnamen anzugeben.Bearbeiten : Es gibt einen Fehler in meiner früheren Argumentation. "Nicht statische Elementfunktionen können auch in nicht bewerteten Kontexten nur mit unqualifiziertem Namen aufgerufen werden, wenn dies verwendet werden kann ([over.call.func] p3)", ist falsch. Was es tatsächlich sagt, ist
Innerhalb einer statischen Elementfunktion wird sie
this
möglicherweise nicht angezeigt, ist jedoch weiterhin vorhanden.Gemäß den Kommentaren wird jedoch innerhalb einer statischen Elementfunktion die Transformation von
f()
to(*this).f()
nicht ausgeführt, und wenn diese nicht ausgeführt wird, wird [expr.call] p1 verletzt:da es keinen Mitgliederzugang geben würde. Selbst das würde nicht funktionieren.
quelle
_self_fn_1()
"transformiert" ist(*this)._self_fn_1()
. Ich bin mir nicht sicher, ob das illegal ist.X
in einem Kontext verwendet, in dem esthis
verwendet werden kann", daher glaube ich nicht, dass eine Transformation durchgeführt wird.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
Überlastungen: Das ist kein Problem. Speziell modifiziert worden , um auch auf statische Elementfunktionen anwenden, und sagt die Art der 5.1p3 hatthis
istFoo*
/Bar*
(ohneconst
), weil es keineconst
in der Erklärung_self_fn_2
.Dies funktioniert nicht bei Vorlagentypen, wie sie
self_check
nicht aufgerufen werden, daherstatic_assert
wird die nicht ausgewertet.Wir können einige Hacks durchführen, damit es auch für
template
s funktioniert , aber es hat nur geringe Laufzeitkosten.struct
In Ihrer Klasse wird ein leeres Byte der Größe 1 erstellt. Wenn Ihr Typ instanziiert ist,self
wird gegen getestet.quelle
template
Optionen zur Klassenunterstützung.inline
. Das heißt, Sie müssen überhaupt nicht schreibeninline
. Wenn Sie alsoinline
während Ihrer gesamten Karriere vor jeder solchen Funktionsdefinition für Klassenmitglieder geschrieben haben, können Sie jetzt aufhören;)Ich denke auch, dass es unmöglich ist, hier ist ein weiterer fehlgeschlagener, aber meiner Meinung nach interessanter Versuch, der den
this
Zugriff vermeidet :Dies schlägt fehl, weil Sie sich in C ++ für
self_f
die Klasse qualifizieren müssen, wenn Sie die Adresse übernehmen möchten :(quelle
int T::*
Zeiger auf die Mitgliedsvariable auf. Undint self_var; typedef decltype(&self_var) self_ptr
funktioniert auch nicht, das ist nur ein normalerint*
.Ich habe kürzlich entdeckt,
*this
dass dies in einem Brace-or-Equal-Initialisierer zulässig ist . Beschrieben in § 5.1.1 ( aus dem Arbeitsentwurf n3337 ):In diesem Sinne den folgenden Code:
geht an Daniel Freys vorbei
static_assert
.Live example
quelle
test
obwohl= this
, oder? Und warum nicht einfachusing self = Foo*;
test
vom Typ sind, ähmFoo *
!Sofern der Typ nicht Mitgliedstyp der einschließenden Klasse sein muss, können Sie die Verwendung von
self
durch ersetzendecltype(*this)
. Wenn Sie es an vielen Stellen in Ihrem Code verwenden, können Sie ein MakroSELF
wie folgt definieren:quelle
self
auf die unmittelbar einschließende Klasse und nicht auf die äußere Klasse beziehen? Aber ich kenne PHP nicht so gut.Geben Sie meine Version an. Das Beste ist, dass seine Verwendung der der einheimischen Klasse entspricht. Es funktioniert jedoch nicht für Vorlagenklassen.
quelle
Aufbauend auf der Antwort von hvd stellte ich fest, dass das einzige, was fehlte, das Entfernen der Referenz war. Deshalb schlägt die Prüfung std :: is_same fehl (b / c ist der resultierende Typ tatsächlich eine Referenz auf den Typ). Jetzt kann dieses parameterlose Makro die ganze Arbeit erledigen. Arbeitsbeispiel unten (ich benutze GCC 8.1.1).
quelle
Ich werde die offensichtliche Lösung wiederholen, "es selbst tun zu müssen". Dies ist die prägnante C ++ 11-Version des Codes, die sowohl mit einfachen Klassen als auch mit Klassenvorlagen funktioniert:
Sie können es bei ideone in Aktion sehen . Die Genese, die zu diesem Ergebnis führt, ist unten:
Dies hat das offensichtliche Problem, den Code in eine andere Klasse einzufügen und zu vergessen, XYZ zu ändern, wie hier:
Mein erster Ansatz war nicht sehr originell - eine Funktion wie diese zu erstellen:
Es ist etwas langwierig, aber bitte nehmen Sie mich hier mit. Dies hat den Vorteil, in C ++ 03 ohne zu arbeiten
decltype
, da die__self_check_helper
Funktion verwendet wird, um den Typ von abzuleitenthis
. Auch gibt es keinestatic_assert
, aber dersizeof()
Trick wird stattdessen angewendet. Sie könnten es für C ++ 0x viel kürzer machen. Dies funktioniert jetzt nicht mehr für Vorlagen. Es gibt auch ein kleines Problem mit dem Makro, das am Ende kein Semikolon erwartet. Wenn es mit pedantisch kompiliert wird, wird es sich über ein zusätzliches unnötiges Semikolon beschweren (oder Sie werden mit einem seltsam aussehenden Makro zurückbleiben, das nicht mit einem Semikolon im Hauptteil vonXYZ
und endetABC
).Eine Überprüfung der
Type
übergebenen DatenDECLARE_SELF
ist keine Option, da dadurch nur dieXYZ
Klasse überprüft wird (was in Ordnung ist), ohne zu wissenABC
(was fehlerhaft ist). Und dann traf es mich. Eine kostengünstige Lösung ohne zusätzlichen Speicher, die mit Vorlagen funktioniert:Dadurch wird lediglich eine statische Zusicherung für einen eindeutigen Aufzählungswert vorgenommen (oder zumindest für den Fall, dass Sie nicht den gesamten Code in eine einzelne Zeile schreiben), es wird kein Trick zum Vergleichen von Typen angewendet, und es funktioniert auch in Vorlagen als statische Zusicherung . Und als Bonus - das letzte Semikolon ist jetzt erforderlich :).
Ich möchte Yakk dafür danken, dass er mich gut inspiriert hat. Ich würde das nicht schreiben, ohne zuerst seine Antwort zu sehen.
Getestet mit VS 2008 und g ++ 4.6.3. In der Tat beschwert es sich mit dem
XYZ
undABC
Beispiel:Wenn wir nun ABC zu einer Vorlage machen:
Wir werden bekommen:
Nur die Zeilennummernprüfung wurde ausgelöst, da die Funktionsprüfung nicht (wie erwartet) kompiliert wurde.
Mit C ++ 0x (und ohne die bösen Unterstriche) benötigen Sie nur:
Ich glaube, dass das CStaticAssert-Bit leider immer noch benötigt wird, da es einen Typ erzeugt, der im Vorlagenkörper typisiert ist (ich nehme an, dass dies nicht möglich ist
static_assert
). Der Vorteil dieses Ansatzes sind immer noch die Nullkosten.quelle
static_assert
hier im Wesentlichen neu , nicht wahr? Darüber hinaus ist Ihr vollständiger Code ungültig, da Sie illegale (reservierte) Bezeichner verwenden.Ich weiß nicht alles über diese verrückten Vorlagen, wie wäre es mit etwas Supereinfachem:
Arbeit erledigt, es sei denn, Sie können ein paar Makros nicht ausstehen. Sie können sogar verwenden
CLASSNAME
Ihre Konstruktoren (und natürlich den Destruktor) deklarieren.Live-Demo .
quelle