Frage

Wie funktioniert die folgenden 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];
  1. Beachten Sie, dass B private Basis. Wie funktioniert das?

  2. Beachten Sie, dass operator B*() ist konst. Warum ist es wichtig?

  3. Warum ist template<typename T> static yes check(D*, T); besser als static yes check(B*, int);?

Hinweis : Es wird reduzierte Version (Makros werden entfernt) von boost::is_base_of. Und das funktioniert bei der breiten Palette von Compilern.

War es hilfreich?

Lösung

Wenn sie verwandt sind

Lassen Sie uns für einen Moment annehmen, dass B ist eigentlich eine Basis von D. Dann für den Anruf zu check sind beide Versionen rentabel, weil Host zu D* umgewandelt werden kann und B*. Es ist eine frei wählbare Konvertierungssequenz, wie jeweils durch 13.3.3.1.2 von Host<B, D> zu D* und B* beschrieben. Für Konvertierungsfunktionen zu finden, die die Klasse umwandeln kann, werden die folgenden Kandidaten Funktionen für die erste check Funktion synthetisiert nach 13.3.1.5/1

D* (Host<B, D>&)

Die erste Konvertierungsfunktion ist kein Kandidat, weil B* nicht zu D* umgewandelt werden kann.

Für die zweite Funktion, die folgenden Kandidaten vorhanden sein:

B* (Host<B, D> const&)
D* (Host<B, D>&)

Das sind die beiden Konvertierungsfunktion Kandidaten, die das Host-Objekt übernehmen. Die erste dauert es durch konstante Referenz, und der zweite nicht. Somit wird der zweite ist eine bessere Übereinstimmung für das nicht-const *this Objekt (das implizites Objekt Argument ) durch 13.3.3.2/3b1sb4 und ist für die zweite B* Funktion zu konvertieren check verwendet.

Wenn Sie Entfernen die const, würden wir folgende Kandidaten haben

B* (Host<B, D>&)
D* (Host<B, D>&)

Dies würde bedeuten, dass wir nicht mehr von Konstantheit auswählen können. Bei einem gewöhnlichen Überladungsauflösung Szenario würde der Anruf jetzt nicht eindeutig sein, da normalerweise der Rückgabetyp nicht in der Überladungsauflösung teilnehmen. Für Konvertierungsfunktionen, gibt es jedoch ein Backdoor. Wenn zwei Konvertierungsfunktionen gleich gut sind, dann entscheidet der Rückgabetyp von ihnen, die am besten nach 13.3.3/1. Wenn Sie also die const entfernen würden, dann der erste genommen werden würde, weil B* Konvertiten besser B* als D* zu B*.

Nun, was benutzerdefinierte Konvertierungssequenz ist besser? Die eine für die zweite oder die erste Überprüfungsfunktion? Die Regel ist, dass definierte Benutzerkonvertierungssequenzen können nur dann, wenn sie nach der gleichen 13.3.3.2/3b2 Umwandlungsfunktion oder Konstruktor verglichen werden. Das ist genau das hier der Fall: Beide verwenden die zweite Konvertierungsfunktion. Beachten Sie, dass damit die const ist wichtig, weil es den Compiler zwingt die zweite Konvertierungsfunktion zu übernehmen.

Da wir sie vergleichen können - was besser ist? Die Regel ist, dass die bessere Umwandlung von dem Rückgabetyp der Konvertierungsfunktion zum Zieltyp gewinnt (wieder durch 13.3.3.2/3b2). In diesem Fall D* wandelt besser D* als zu B*. Damit ist die erste Funktion ausgewählt ist und wir erkennen das Erbe!

, dass Hinweis, da wir nie benötigt wirklich convert auf eine Basisklasse, können wir dadurch erkennen private Vererbung , da, ob wir von einem D* zu einem B* isn zu konvertieren‘ t abhängig von der Form der Vererbung nach 4.10/3

Wenn sie nicht im Zusammenhang

sind

Nun wollen wir annehmen, dass sie nicht durch Vererbung verwandt sind. So für die erste Funktion haben wir folgende Kandidaten

D* (Host<B, D>&) 

Und zum zweiten haben wir nun einen weiteren Satz

B* (Host<B, D> const&)

Da wir nicht D* zu B* umwandeln können, wenn wir nicht eine Vererbungsbeziehung erhalten haben, haben wir jetzt keine gemeinsame Umsetzungsfunktion zwischen den zwei benutzerdefinierten Umwandlungsfolgen! So würden wir mehrdeutig , wenn nicht für die Tatsache sein, dass die erste Funktion ist eine Vorlage. Vorlagen sind zweite Wahl, wenn es eine Nicht-Template-Funktion ist, die gemäß 13.3.3/1 gleich gut ist. So wählen wir die Nicht-Template-Funktion (zweite) und wir erkennen, dass es keineVererbung zwischen B und D!

Andere Tipps

Lassen Sie uns arbeiten, wie es funktioniert, indem bei den Schritten suchen.

Starten Sie mit dem sizeof(check(Host<B,D>(), int())) Teil. Der Compiler kann schnell sehen, dass dieser check(...) ein Funktionsaufruf Ausdruck ist, so ist es Auflösung auf check tun Überlastung muss. Es gibt zwei Kandidaten Überlastungen zur Verfügung, template <typename T> yes check(D*, T); und no check(B*, int);. Wenn der erste gewählt wird, erhalten Sie sizeof(yes), sonst sizeof(no)

Als nächstes wollen wir einen Blick auf die Überladungsauflösung. Die erste Überlast ist eine Schablone Instanziierung check<int> (D*, T=int) und der zweite Kandidat ist check(B*, int). Die tatsächlichen Argumente vorgesehenen Host<B,D> und int(). Der zweite Parameter ist klar unterscheiden sie nicht; es lediglich diente die erste Überlast einer Vorlage zu machen. Wir werden später sehen, warum die Vorlage Teil relevant ist.

Nun ein Blick auf die Umwandlung Sequenzen, die benötigt werden. Zum ersten Überlastung, wir haben Host<B,D>::operator D* - eine benutzerdefinierte Konvertierung. Zum zweiten ist die Überlastung kniffliger. Wir brauchen einen B *, aber es gibt möglicherweise zwei Umwandlungsfolgen. Eine davon ist über Host<B,D>::operator B*() const. Wenn (und nur dann) B und D durch Vererbung wird die Umwandlungsfolge Host<B,D>::operator D*() + D*->B* exist bezogen. Man nehme nun an D in der Tat erbt von B. Die beiden Umwandlungsfolgen sind Host<B,D> -> Host<B,D> const -> operator B* const -> B* und Host<B,D> -> operator D* -> D* -> B*.

Also, für verwandte B und D, no check(<Host<B,D>(), int()) würde nicht eindeutig. Als Ergebnis wird das Templat yes check<int>(D*, int) ausgewählt. Wenn jedoch D nicht von B erbt, dann no check(<Host<B,D>(), int()) ist nicht eindeutig. An diesem Punkt kann die Überladungsauflösung nicht auf kürzeste Umwandlungsfolge baed happen. Angesichts der Tatsache gleich Umwandlungsfolgen, Überladungsauflösung bevorzugt nicht-Template-Funktionen, d.h. no check(B*, int).

Sie jetzt sehen, warum spielt es keine Rolle, dass die Vererbung privat ist: daß die Beziehung dient nur no check(Host<B,D>(), int()) vor Überlastung Auflösung zu beseitigen, bevor die Zugriffsprüfung passiert. Und Sie auch, warum die operator B* const const sein muss. Sonst gibt es keine Notwendigkeit für den Host<B,D> -> Host<B,D> const Schritt, ohne Zweideutigkeit, und no check(B*, int) würde immer gewählt wird

Das private Bit vollständig von is_base_of ignoriert, da der Überladungsauflösung vor Zugänglichkeit überprüft auftritt.

können Sie überprüfen, diese einfach:

class Foo
{
public:
  void bar(int);
private:
  void bar(double);
};

int main(int argc, char* argv[])
{
  Foo foo;
  double d = 0.3;
  foo.bar(d);       // Compiler error, cannot access private member function
}

Auch hier gilt die Tatsache, dass B eine private Basis ist die Prüfung nicht daran hindert, stattfindet, wäre es nur um die Umwandlung zu verhindern, aber wir haben nie für die eigentliche Umsetzung fragen;)

Es hat vielleicht etwas mit teilweise Ordnung w.r.t. zu tun Überladungsauflösung. D * ist mehr spezialisiert als B * bei D ergibt sich aus B.

Die genauen Details sind ziemlich kompliziert. Sie müssen die Präzedenzfälle von verschiedenen Überladungsauflösung Regeln herauszufinden. Partielle Ordnung ist eins. Längen / Arten von Umwandlungs Sequenzen ist eine andere. Schließlich, wenn zwei lebensfähige Funktionen gelten als gleich gut, nicht-Vorlagen über Funktionsschablonen ausgewählt werden.

Ich habe noch nie sehen benötigt, wie diese Regeln in Wechselwirkung treten. Aber es scheint, teilweise Ordnung, die andere Überladungsauflösung Regeln dominiert. Wenn D nicht leitet sich aus B die Teilordnungsregeln nicht gelten und die nicht-Vorlage ist attraktiver. Wenn D ergibt sich aus B, teilweise Ordnung Tritte in und macht die Funktion Vorlage attraktiver -. Wie es scheint,

Wie für die Vererbung ist privete. Der Code fragt nie für eine Umwandlung von D * auf B *, die die öffentliche inheritence erfordern würde

Im Anschluss an Ihrer zweiten Frage, beachten Sie, dass, wenn es nicht für const ist, Host schlecht gebildet instanziert werden würde, wenn mit B == D. Aber is_base_of ist so ausgelegt, dass jede Klasse ist eine Basis von selbst, also eines von Umwandlung Betreiber müssen const sein.

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