15

Im Moment lerne ich die Funktionen der Vererbung in C++ und wollte das kürzlich erlernte Konzept der Virtual Base-Klassen testen. Ich habe versucht, die folgenden einfachen Code:Warum verhält sich die Initialisierung in C++ 11 bei virtuellen Basisklassen merkwürdig?

#include <iostream> 

using namespace std; 

class A 
{ 
private: 
    int m_value; 
    string m_caller; 
public: 
    A(int p_value, string p_caller) : m_value{p_value}, m_caller{p_caller} 
    { 
     cout<<"Instantiating A via "<<m_caller<<endl; 
    } 
}; 

class B : virtual public A 
{ 
private: 
    int m_value; 
public: 
    B(int p_value1,int p_value2) : A{p_value1,"B"}, m_value{p_value2} 
    { 
     cout<<"Instantiating B."<<endl; 
    } 
}; 

class C : public B 
{ 
public: 
    C(int p_value1,int p_value2) : A{p_value1,"C"}, B(p_value1, p_value2) 
    { 
     cout<<"Instantiating C."<<endl; 
    } 
}; 

int main() 
{ 
    C c1(1,2); 
    return 0; 
} 

Bitte beachten Sie die B(p_value1, p_value2) im Konstruktor der Klasse C. Dies gab mir die gewünschte Ausgabe:

Instantiating A via C 
Instantiating B. 
Instantiating C. 

Aber der Moment, als ich es zu B{p_value1, p_value2} geändert, ich habe die folgende Ausgabe:

Instantiating A via C 
Instantiating A via B 
Instantiating B. 
Instantiating C. 

ich habe versucht, für die Antwort suchen, aber alle Antworten, die ich habe einige C++ Standards zitiert . Als Anfänger in OOPs suche ich nach einer einfacheren Erklärung für dieses Verhalten. Vielen Dank!

P.S. Ich verwende C :: B in Windows mit Compiler g ++ 4.8.1.

+3

Fyi, clang ++ 3.7 zeigt nicht das Verhalten, das Sie beschreiben. Beide Versionen geben die gleiche Ausgabe aus (die erste). – WhozCraig

+0

Sie rufen den Kopierkonstruktor im zweiten Fall auf. – lorro

+3

@UpAndAdam Die am weitesten abgeleitete Klasse ist verantwortlich für die Initialisierung von virtuellen Basisklassen, der Code würde nicht kompilieren, wenn das OP den Konstruktor von 'A' nicht direkt aufruft. Und was ist das mit * übergeben konvertierten String-Literale, als ob sie Ints * sind? – Praetorian

Antwort

8

Dies ist ein Compilerfehler in g ++.

In C++ 14 (N4140) Abschnitt [dcl.init.list], ist die Definition der Liste Initialisierung (für Prägnanz edited):

List-Initialisierung eines Objekts oder einer Referenz des Typs T ist wie folgt definiert:

  • wenn T ein Aggregat ist, aggregieren die Initialisierung
  • Andernfalls durchgeführt wird, wenn die Initialisiererliste keine Elemente hat und T ist ein Klassentyp mit einem Standard-Konstruktor , das Objekt wird value-initialisiert. Ansonsten
  • , wenn T ist eine Spezialisierung von std :: initializer_list [...]
  • Andernfalls, wenn T ein Klassentyp ist, sind Konstrukteure in Betracht gezogen. Die anwendbaren Konstruktoren werden aufgelistet und der beste wird durch Überladungsauflösung ausgewählt. Wenn eine konvergierende Konvertierung erforderlich ist, um eines der Argumente zu konvertieren, ist das Programm schlecht formatiert.
  • [...]

Die ersten 3 Punkte gelten nicht: B ist kein Aggregat (Aggregat kann nicht Basisklassen haben), hat die Initialisiererliste Elemente, B ist keine Spezialisierung von std::initializer_list.

Der vierte Punkt gilt wegen Überlastung Auflösung B{p_value1, p_value2} an den Konstruktor übereinstimmt B(int, int) nach [over.match.list] /1.2:

Wenn keine tragfähige Initialisierer-Liste Konstruktor gefunden wird, die Überladungsauflösung ausgeführt wird wieder, wo die Kandidatenfunktionen alle Konstruktoren der Klasse T sind und die Argumentliste aus den Elementen der Initialisierungsliste besteht.

Es folgt aus dem letzten Zitat, dass B(whatever) und B{whatever} identisch verhalten sollten.

+0

Perfekte Antwort! Vielen Dank :) – SimplyOm