Hur fungerar `is_base_of`?

Hur fungerar `is_base_of`?

Om de är relaterade

Låt oss för ett ögonblick anta att B är faktiskt en bas av D . Sedan för samtalet till check , båda versionerna är användbara eftersom Host kan konverteras till D* och B* . Det är en användardefinierad konverteringssekvens som beskrivs av 13.3.3.1.2 från Host<B, D> till D* och B* respektive. För att hitta konverteringsfunktioner som kan konvertera klassen, syntetiseras följande kandidatfunktioner för de första check fungerar enligt 13.3.1.5/1

D* (Host<B, D>&)

Den första konverteringsfunktionen är inte en kandidat, eftersom B* kan inte konverteras till D* .

För den andra funktionen finns följande kandidater:

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

Det är de två konverteringsfunktionskandidaterna som tar värdobjektet. Den första tar det genom konstreferens, och den andra gör det inte. Således är den andra en bättre matchning för icke-konst *this objekt (det implicita objektargumentet ) med 13.3.3.2/3b1sb4 och används för att konvertera till B* för den andra check fungera.

Om du skulle ta bort const, skulle vi ha följande kandidater

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

Detta skulle innebära att vi inte längre kan välja efter konstitet. I ett vanligt överbelastningsscenario skulle samtalet nu vara tvetydigt eftersom returtypen normalt inte deltar i överbelastningslösningen. För konverteringsfunktioner finns det dock en bakdörr. Om två konverteringsfunktioner är lika bra, så avgör returtypen för dem vem som är bäst enligt 13.3.3/1 . Således, om du skulle ta bort const, så skulle den första tas, eftersom B* konverterar bättre till B* än D* till B* .

Vilken användardefinierad konverteringssekvens är nu bättre? Den för den andra eller den första kontrollfunktionen? Regeln är att användardefinierade konverteringssekvenser endast kan jämföras om de använder samma konverteringsfunktion eller konstruktor enligt 13.3.3.2/3b2 . Detta är exakt fallet här:Båda använder den andra konverteringsfunktionen. Lägg märke till att alltså konst är viktigt eftersom det tvingar kompilatorn att ta den andra konverteringsfunktionen.

Eftersom vi kan jämföra dem - vilken är bättre? Regeln är att den bättre konverteringen från returtypen för konverteringsfunktionen till destinationstypen vinner (igen med 13.3.3.2/3b2 ). I det här fallet, D* konverterar bättre till D* än till B* . Därmed är den första funktionen vald och vi känner igen arvet!

Lägg märke till att eftersom vi aldrig behövde faktiskt konvertera till en basklass kan vi därigenom känna igen privat arv eftersom om vi kan konvertera från en D* till en B* är inte beroende av arvsformen enligt 4.10/3

Om de inte är relaterade

Låt oss nu anta att de inte är släkt med arv. För den första funktionen har vi alltså följande kandidater

D* (Host<B, D>&) 

Och för den andra har vi nu ett annat set

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

Eftersom vi inte kan konvertera D* till B* om vi inte har ett arvsförhållande har vi nu ingen gemensam konverteringsfunktion bland de två användardefinierade konverteringssekvenserna! Därför skulle vi vara tvetydiga om inte för det faktum att den första funktionen är en mall. Mallar är andrahandsval när det finns en icke-mallfunktion som är lika bra enligt 13.3.3/1 . Därför väljer vi funktionen icke-mall (andra) och vi inser att det inte finns något arv mellan B och D !


Låt oss ta reda på hur det fungerar genom att titta på stegen.

Börja med sizeof(check(Host<B,D>(), int())) del. Kompilatorn kan snabbt se att detta check(...) är ett funktionsanropsuttryck, så det måste göra överbelastningsupplösning på check . Det finns två kandidatöverbelastningar tillgängliga, template <typename T> yes check(D*, T); och no check(B*, int); . Om den första väljs får du sizeof(yes) , annars sizeof(no)

Låt oss sedan titta på överbelastningsupplösningen. Den första överbelastningen är en mallinstansiering check<int> (D*, T=int) och den andra kandidaten är check(B*, int) . De faktiska argumenten som tillhandahålls är Host<B,D> och int() . Den andra parametern skiljer dem inte åt; det tjänade bara till att göra den första överbelastningen till en mall. Vi får se senare varför malldelen är relevant.

Titta nu på de konverteringssekvenser som behövs. För den första överbelastningen har vi Host<B,D>::operator D* - en användardefinierad konvertering. För det andra är överbelastningen svårare. Vi behöver ett B*, men det finns möjligen två omvandlingssekvenser. En är via Host<B,D>::operator B*() const . Om (och endast om) B och D är relaterade genom arv kommer konverteringssekvensen Host<B,D>::operator D*() + D*->B* existera. Antag nu att D verkligen ärver från B. De två konverteringssekvenserna är Host<B,D> -> Host<B,D> const -> operator B* const -> B* och Host<B,D> -> operator D* -> D* -> B* .

Så för relaterade B och D, no check(<Host<B,D>(), int()) skulle vara tvetydig. Som ett resultat, mallen yes check<int>(D*, int) är vald. Men om D inte ärver från B, då no check(<Host<B,D>(), int()) är inte tvetydig. Vid denna tidpunkt kan överbelastningsupplösning inte ske baserat på den kortaste konverteringssekvensen. Men givet lika konverteringssekvenser föredrar överbelastningsupplösning icke-mallfunktioner, dvs no check(B*, int) .

Du ser nu varför det inte spelar någon roll att arvet är privat:den relationen tjänar bara till att eliminera no check(Host<B,D>(), int()) från överbelastningsupplösning innan åtkomstkontrollen sker. Och du ser också varför operator B* const måste vara const:annars finns det inget behov av Host<B,D> -> Host<B,D> const steg, ingen tvetydighet och no check(B*, int) skulle alltid väljas.


private bit ignoreras helt av is_base_of eftersom överbelastningslösning sker innan tillgänglighetskontroller.

Du kan enkelt verifiera detta:

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
}

Detsamma gäller här, det faktum att B är en privat bas hindrar inte kontrollen från att ske, det skulle bara förhindra konverteringen, men vi ber aldrig om själva konverteringen;)