2013-07-08 4 views
5

Ich habe eine rautenförmige Hierarchie von Klassen, in denen es weder einen Standardkonstruktor noch Kopierkonstruktoren gibt. Die beiden Konstrukteure ich habe, sind eine „Bewegung“ ein und eine andere, die einen L-Wert-Referenz auf ein Objekt nimmt:Wie implementiert man einen Move-Konstruktor für eine diamantförmige Vererbung?

struct base { 
    base(base&&) = default; 
    base(member_type&& m): member_(std::move(m)) {} 
    member_type member_; 
}; 

struct virt_1: virtual base { 
    virt_1(virt_1&& rhs): base(std::move(rhs)) {} 
    virt_1(member_type&& m): base(std::move(m)) {} 
}; 

struct virt_2: virtual base { 
    virt_2(virt_2&& rhs): base(std::move(rhs)) {} 
    virt_2(member_type&& m): base(std::move(m)) {} 
}; 

struct concrete: virt_1, virt_2 { 
    concrete(concrete&& rhs) // ??? 
}; 

Neben keinen rautenförmigen Hierarchie verwendet wird, ist es möglich, die Bewegung Konstruktor für die konkrete Klasse zu implementieren ?

Danke!

+1

Klingt sehr fehleranfällig für mich. Der Konstruktor der virtuellen Basis wird von der am weitesten abgeleiteten Klasse aufgerufen, und es sollte keinen Grund geben, warum er keinen Verschiebungskonstruktor aufrufen konnte. Aber jede weitere Ableitung, und es besteht eine gute Chance, dass jemand es vergessen wird, oder es falsch macht. Als allgemeine Regel sollten virtuelle Basisklassen keine Daten enthalten oder zumindest andere Konstruktoren als einen Standardkonstruktor haben, um solche Probleme zu vermeiden. –

+0

@JamesKanze Ich bin mir bewusst, dass "Basis" sollte keine Daten enthalten, oder zumindest einen Standardkonstruktor, aber es kann nicht der Fall in meinem Kontext sein. Gibt es jedoch eine Möglichkeit, dass der Compiler sich beschweren wird, wenn jemand vergisst, den Konstruktor der virtuellen Basis aufzurufen, wenn er weiter ableitet? – piwi

+0

@piwi, nein, es gibt keine Chance, weil 'base' keinen Standardkonstruktor hat, also muss der am meisten abgeleitete Typ ** es explizit konstruieren –

Antwort

8

Was ist falsch daran, den Compiler nach der Implementierung zu fragen?

concrete(concrete&&) = default; 

Ich würde definieren die virt_1 und virt_2 Konstrukteurs bewegen als zu Verzug geraten.

Man könnte es schreiben, wenn Sie wirklich wollte:

concrete(concrete&& rhs) 
: base(std::move(rhs)), virt_1(std::move(rhs)), virt_2(std::move(rhs)) 
{ } 

Oder wenn Sie wirklich, wie eingeben:

concrete(concrete&& rhs) 
    : base(static_cast<base&&>(rhs)), 
     virt_1(static_cast<virt_1&&>(rhs)), 
     virt_2(static_cast<virt_2&&>(rhs)) 
    { } 

Die Initialisierungen für die virt_1 und virt_2 Basen nutzlos sind, weil sie nur Rufen Sie den base Konstruktor auf, und da es eine virtuelle Basis ist, werden sie das nicht tun, wenn concrete es aufgerufen hat, aber aufgrund Ihrer Auswahl von Konstruktoren können Sie sie nicht standardmäßig konstruieren und müssen sie mit einem initialisieren rvalue, obwohl sie nichts damit machen.

+1

Frage: wäre das nicht gefährlich? Sieht so aus, als ob es sich mehrfach vom selben Objekt bewegt ... –

+0

Ich denke, dass mein Problem in beiden Konstruktoren der konkreten Klasse liegt; Ich werde mit dem Standard-Move-Konstruktor – piwi

+1

@AndyProwl sehen, nein, siehe den letzten Absatz, den ich hinzugefügt habe. Der 'base'-Konstruktor wird nur einmal aufgerufen (vom Konstruktor für den am weitesten abgeleiteten Typ), so dass alle anderen Konstruktoren nichts mit ihrem rvalue-Parameter tun. Es wird nur eine Bewegung geben (bedenke, dass das Aufrufen von 'std :: move' eigentlich nichts bewegt, eine Bewegung nur passiert, wenn etwas einen rvalue-Parameter akzeptiert und sich davon entfernt.) –

1

Sicher. Jeder Konstruktor mit einer virtual Basis irgendwo in der Hierarchie ist verantwortlich für die Initialisierung dieser Basis. Im Wesentlichen erbt jede Klasse unterhalb der virtual Basis in der Hierarchie die Berechtigung zum Initialisieren der Basis.

In diesem Fall sollte der Standard-Move-Konstruktor das Richtige tun. Ich würde auch empfehlen, concrete : private virtual base zu spezifizieren, um zu klären, was passiert.

Here ist eine funktionierende Demo.

+0

Könnte den Bewegungszuweisungsoperator auch gut prüfen. –

+1

Die Klasse muss nicht direkt von der Basis erben. Es ist jedoch dafür verantwortlich, die Konstruktoren jeder virtuellen Basis aufzurufen, selbst wenn sie nicht von ihnen erbt. –

+0

@JamesKanze Oh, ich dachte, es wäre ein Syntaxfehler, einen Mem-Initialisierer für eine indirekte Basis zu haben. Es ist nicht. Korrigiere mich trotzdem, wenn ich falsch liege, aber der Unterschied ist nur Syntax/Stil. Es würde keinen Unterschied machen, von der Basis zu erben, und es wäre illegal, von der Basis ohne "virtuell" zu erben. – Potatoswatter

Verwandte Themen