Wie funktioniert `is_base_of`?
Wie funktioniert der folgende code funktioniert?
typedef char (&yes)[1];
typedef char (&no)[2];
template <typename B, typename D>
struct Host
{
operator B*() const;
operator D*();
};
template <typename B, typename D>
struct is_base_of
{
template <typename T>
static yes check(D*, T);
static no check(B*, int);
static const bool value = sizeof(check(Host<B,D>(), int())) == sizeof(yes);
};
//Test sample
class Base {};
class Derived : private Base {};
//Expression is true.
int test[is_base_of<Base,Derived>::value && !is_base_of<Derived,Base>::value];
- Beachten Sie, dass
B
privater Basis. Wie funktioniert das? - Beachten Sie, dass
operator B*()
ist const. Warum ist es wichtig? - Warum ist
template<typename T> static yes check(D*, T);
besser alsstatic yes check(B*, int);
?
Hinweis: Es ist die reduzierte version (Makros werden entfernt)boost::is_base_of
. Und das funktioniert auf Breite Palette von Compilern.
Kommentar zu dem Problem
Es ist sehr verwirrend von Ihnen verwenden den gleichen Bezeichner für eine template-parameter und eine echte Klasse name...
@Matthieu M., ich habe es genommen, auf mich zu korrigieren 🙂
Vor einiger Zeit schrieb ich eine alternative Implementierung von
is_base_of
: ideone.com/T0C1V Es funktioniert nicht mit älteren GCC-Versionen, aber (GCC4.3 gut funktioniert). Ok, ich werde einen Spaziergang zu machen.
Diese Implementierung ist nicht korrekt.
is_base_of<Base,Base>::value
sollte true
; dies gibt false
. InformationsquelleAutor der Frage Alexey Malistov | 2010-05-26
Du musst angemeldet sein, um einen Kommentar abzugeben.
, Wenn Sie verwandt sind
Lasst uns für einen moment davon ausgehen, dass
B
ist tatsächlich eine Basis vonD
. Dann für den Aufrufcheck
beide Varianten sind sinnvoll, weilHost
umgewandelt werden können, umD*
undB*
. Es ist eine benutzerdefinierte Konvertierung-Sequenz, wie beschrieben, durch13.3.3.1.2
ausHost<B, D>
zuD*
undB*
beziehungsweise. Für die Suche nach Funktionen für die Konvertierung konvertieren kann die Klasse, die folgenden Kandidaten-Funktionen synthetisiert werden, die für die erstecheck
Funktion nach13.3.1.5/1
Dem ersten Konvertierungs-Funktion ist nicht Kandidat, weil
B*
können nicht umgewandelt werden, umD*
.Für die zweite Funktion, die folgenden Kandidaten sind:
Sind die zwei Funktion zur Umsetzung von Kandidaten, die den host-Objekt. Die erste dauert es von const-Referenz, und die zweite nicht. Also das zweite ist eine bessere übereinstimmung für die non-const
*this
Objekt (die implizite Objekt-argument) von13.3.3.2/3b1sb4
und wird verwendet, um zu konvertierenB*
für die zweitecheck
Funktion.Wenn Sie entfernen die const, wir hätten die folgenden Bewerber
Dies würde bedeuten, dass wir nicht auswählen können, indem Sie constness mehr. In eine normale überlast Auflösung Szenario, würde der Aufruf jetzt zweideutig sein, weil normalerweise der Rückgabetyp nicht teilnehmen überlast Auflösung. Für die Konvertierung Funktionen, allerdings gibt es eine Hintertür. Wenn zwei Konvertierungs-Funktionen sind gleich gut, dann ist der Rückgabe-Typ von Ihnen entscheidet, wer am besten nach
13.3.3/1
. Wenn Sie also entfernen würde, der const ist, dann wird die erste genommen werden würde, weilB*
konvertiert besser zuB*
alsD*
zuB*
.Nun, was Benutzer definierten conversion-Sequenz ist besser? Die eine für die zweite oder die erste Funktion prüfen? Die Regel ist, dass Benutzer definierten conversion-Sequenzen können nur verglichen werden, wenn Sie verwenden die gleiche Konvertierung Funktion oder eines Konstruktors nach
13.3.3.2/3b2
. Das ist genau der Fall hier: Sowohl die zweite Konvertierung Funktion. Beachten Sie, dass damit die const ist wichtig, denn es zwingt den compiler, die zweite zu nehmen-Konvertierung Funktion.Da können wir vergleichen - welche ist besser? Die Regel ist, dass der bessere Konvertierung von der Rückgabetyp der Funktion zur Umsetzung der Ziel-Typ gewinnt (wieder durch
13.3.3.2/3b2
). In diesem FallD*
konvertiert besser zuD*
alsB*
. Damit ist die erste Funktion ausgewählt ist, und wir erkennen das Erbe!Bemerken, dass, da wir nie benötigt, um eigentlich Konvertierung in eine Basis-Klasse, so können wir erkennen, private Vererbung da, ob wir Sie umwandeln können, von einem
D*
zu einemB*
ist nicht abhängig von der form der Vererbung nach4.10/3
Wenn Sie nicht mit
Nun nehmen wir an, Sie sind nicht im Zusammenhang mit Vererbung. Also für die erste Funktion haben wir folgende Kandidaten
Und für die zweite haben wir jetzt einen anderen Satz
Da wir Sie nicht konvertieren
D*
zuB*
wenn wir nicht haben ein Erbe Beziehung, wir haben jetzt keine Allgemeine Konvertierung-Funktion bei der zwei Benutzer-definierten conversion-Sequenzen! Also, wir würden uns mehrdeutig, wenn nicht für die Tatsache, dass die erste Funktion ist eine Vorlage. Vorlagen sind zweite Wahl, wenn es eine nicht-template-Funktion, die genauso gut nach13.3.3/1
. So wählen wir die nicht-template-Funktion (zweite) und wir erkennen, dass es keine Vererbung zwischenB
undD
!InformationsquelleAutor der Antwort Johannes Schaub - litb
Lassen Sie uns herausfinden, wie es funktioniert, indem man die Schritte.
Beginnen Sie mit der
sizeof(check(Host<B,D>(), int()))
Teil. Der compiler schnell erkennen können, dass diesecheck(...)
ist ein Funktionsaufruf, Ausdruck, so muss es zu tun überlast Auflösung aufcheck
. Es gibt zwei Kandidaten überladungen zur Verfügung,template <typename T> yes check(D*, T);
undno check(B*, int);
. Wenn das erste ausgewählt wird, erhalten Siesizeof(yes)
, sonstsizeof(no)
Als Nächstes schauen wir uns die überlast Auflösung. Die erste überladung ist eine template-Instantiierung
check<int> (D*, T=int)
und der zweite Kandidat istcheck(B*, int)
. Die eigentlichen Argumente sindHost<B,D>
undint()
. Der zweite parameter offensichtlich nicht unterscheiden; nur diente, um die ersten überlast-Vorlage. Wir werden später sehen, warum die Vorlage Teil ist relevant.Nun einen Blick auf die conversion-Sequenzen, die benötigt werden. Für die erste überlast, wir haben
Host<B,D>::operator D*
- eine benutzerdefinierte Konvertierung. Für die zweite, die überlastung ist schwieriger. Wir brauchen eine B*, aber es gibt möglicherweise zwei conversion-Sequenzen. Man ist perHost<B,D>::operator B*() const
. Wenn (und nur wenn) B und D sind im Zusammenhang mit Vererbung wird die Umwandlung SequenzHost<B,D>::operator D*()
+D*->B*
vorhanden. Nehmen Sie jetzt D in der Tat erbt von B. Die beiden conversion-Sequenzen sindHost<B,D> -> Host<B,D> const -> operator B* const -> B*
undHost<B,D> -> operator D* -> D* -> B*
.So, für Verwandte B und D,
no check(<Host<B,D>(), int())
würde mehrdeutig. Als Ergebnis, die Vorlagenyes check<int>(D*, int)
gewählt wird. Jedoch, wenn D nicht Erben von B, dannno check(<Host<B,D>(), int())
ist nicht mehrdeutig. An diesem Punkt, überlast-Auflösung kann nicht passieren, baed auf kürzeste Umwandlung der Sequenz. Jedoch, bei gleicher conversion-Sequenzen, überlast Auflösung bevorzugt nicht-template-Funktionen, D. H.no check(B*, int)
.Sehen Sie jetzt, warum es egal ist, dass die Vererbung ist privat: das Verhältnis dient nur zu beseitigen
no check(Host<B,D>(), int())
von overload resolution vor access check passiert. Und Sie sehen auch, warum dieoperator B* const
muss const: sonst gibt es keine Notwendigkeit für dieHost<B,D> -> Host<B,D> const
Schritt, keine Zweideutigkeit, undno check(B*, int)
würde immer gewählt werden.InformationsquelleAutor der Antwort MSalters
Den
private
bit wird komplett ignoriertis_base_of
wegen überlast Auflösung tritt auf, bevor der Barrierefreiheit überprüft.Überprüfen können Sie dies einfach:
Hier gilt das gleiche, die Tatsache, dass
B
ist eine private Basis nicht verhindern, dass die Prüfung stattfindet, es würde nur verhindern, dass die Umwandlung, aber wir Fragen Sie nie für die eigentliche Konvertierung 😉InformationsquelleAutor der Antwort Matthieu M.
Es hat möglicherweise etwas zu tun mit partiellem Bestell-w.r.t. überlast Auflösung. D* ist mehr als spezialisierte B* im Fall D leitet sich von B.
Die genauen details sind ziemlich kompliziert. Sie haben, um herauszufinden, die prš verschiedener überlast Auflösung Regeln. Teilweise ist die Reihenfolge. Längen/Arten der Umwandlung von Sequenzen ist eine andere. Schließlich, wenn zwei brauchbare Funktionen werden als gleich gute, nicht-Vorlagen gewählt werden, die über die Funktion Vorlagen.
Habe ich noch nie benötigt, zu schauen, wie diese Regeln miteinander interagieren. Aber es scheint teilweise ist die Reihenfolge Dominieren die anderen überlast Auflösung Regeln. Wenn D nicht ableiten, von B die partial-ordering-Regeln nicht gelten und die nicht-Vorlage ist attraktiver. Wenn D leitet sich von B, teilweise Bestellung in tritt und macht die Funktion Vorlage mehr attraktiv-wie es scheint.
Als für die Vererbung wird privete: der code fragt niemals für eine Umstellung von der D* B* die öffentlichkeit erfordern würde inheritence.
InformationsquelleAutor der Antwort sellibitze
Folgenden auf Ihre zweite Frage, beachten Sie, dass wenn es nicht für const Host wäre schlecht gebildet, wenn instanziiert mit B == D. Aber is_base_of ist so ausgelegt, dass jede Klasse ist eine Basis von selbst, daher ist eine Konvertierung müssen die Betreiber const.
InformationsquelleAutor der Antwort Hertz