2016-06-02 5 views
3

Betrachten Sie folgenden Code ein:C++, vererbte Kopie ctors funktioniert nicht?

class TBase { 
public: 
    TBase(); 
    TBase(const TBase &); 
}; 

class TDerived: public TBase { 
public: 
    using TBase::TBase; 
}; 

void f() { 
    TBase Base; 
    TDerived Derived(Base); // <=== ERROR 
} 

so habe ich Basis und abgeleiteten Klassen, und will „mit TBase :: TBase“ verwenden, kopiert ctor von Basisklasse ziehen zu können Instanz von abgeleiteten Klasse schaffen, in solche Art und Weise:

TDerived Derived(Base); 

Aber all compilers lehnt dies mit diesen Fehlermeldungen

7 : note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'TBase' to 'const TDerived' for 1st argument 

Warum? Was mache ich falsch? Warum funktioniert "TBase :: TBase" in dieser Situation nicht?

UPDATE Wie kann folgende Stück Code von cppreference.com erklären?

struct B1 { 
    B1(int); 
}; 
struct D1 : B1 { 
    using B1::B1; 
// The set of inherited constructors is 
// 1. B1(const B1&) 
// 2. B1(B1&&) 
// 3. B1(int) 
+0

Weil spezielle Member-Funktionen nicht vererbt werden? Das würde keinen Sinn ergeben. Womit du enden würdest, wäre kein "Copy Constructor". –

Antwort

8

Kopieren und Verschieben von Konstruktoren (und der Standardkonstruktor) werden niemals vererbt, nur weil der Standard dies sagt. Alle anderen Konstruktoren sind.

Dieser Kommentar zu cppreference war irreführend (1). Der gleiche Kommentar im Standard sagt:

Der Kandidat vererbter Konstrukteure setzen in D1 für B1 ist

(Hervorhebung von mir).

Der Standard geht dann davon aus, dass nur der D1(int) Konstruktor tatsächlich vererbt wird. Die Konstruktoren für Kopieren und Verschieben für D1 werden implizit deklariert wie für jede andere Klasse, nicht geerbt.

Weitere Informationen finden Sie in C++ 14 12.9 [class.inhctor].


(1) legte ich eine Änderung cppreference zu hoffentlich zu klären.

1

gemäß Norm 12.6.3/p1 Initialisierung durch vererbte Konstruktor [class.inhctor.init] (Emphasis-Mine):

Wenn ein Konstruktor für Typ B aufgerufen wird, um ein Objekt eines anderen Typs D zu initialisieren (dh, wenn der Konstruktor geerbt wurde (7.3.3)), wird die Initialisierung so fortgesetzt, als ob ein Standardstandard constr Uctor wurde verwendet, um das -Objekt und jede Basisklasse -Unterobjekt zu initialisieren, von dem der Konstruktor geerbt wurde, außer dass das B -Unterobjekt durch den Aufruf des geerbten -Konstruktors initialisiert wird. Die vollständige Initialisierung wird als einzelner Funktionsaufruf betrachtet; insbesondere wird die Initialisierung der ererbten Konstruktorparameter vor der Initialisierung eines beliebigen Teils des Objekts sequenziert.

Daher werden Konstruktoren nicht tatsächlich vererbt, sondern sie werden implizit oder explizit vom jeweiligen abgeleiteten Konstruktor aufgerufen. Beachten Sie auch, dass die geerbten Konstruktoren einfach die Basiskonstruktoren aufrufen und keine Elementinitialisierung im abgeleiteten Objekt ausführen.

Um dies zu verdeutlichen folgendes Beispiel:

struct Base { 
    Base(int); 
    ... 
}; 

struct Derived : Base { 
    using Base::Base; 
    ... 
}; 

Die obige Derived Klassendefinition ist syntaktisch äquivalent mit:

struct Derived : Base { 
    Derived(int i) : Base(i) {} 
    ... 
}; 

Das heißt, die using Deklaration in der Derived Klasse definiert implizit den Konstruktor Derived(int). Beachten Sie an dieser Stelle auch, dass das Programm schlecht formatiert ist, wenn der Konstruktor von mehreren abgeleiteten Basisklassen-Unterobjekten geerbt wird.

Auf die gleiche Art und Weise Sie zu dem logischen Schluss führen worden bin, da ich habe eine Kopie Konstruktor mit der using-Deklaration in der Basisklasse deklariert:

class TBase { 
public: 
    TBase(); 
    TBase(const TBase &); 
}; 

class TDerived: public TBase { 
public: 
    using TBase::TBase; 
}; 

Ich würde das folgende syntaktische Äquivalent erhalten Derived Klasse:

class TDerived: public TBase { 
public: 
    TDerived() : Base() {} 
    TDerived(TBase const &other) : Base(other) {} 
}; 

Dies ist jedoch nicht der Fall. Sie können einen Kopierkonstruktor weder als Standardkonstruktor noch als Bewegungskonstruktor "erben". Warum? Denn so schreibt der C++ - Standard dies vor.

Was können Sie stattdessen zu tun ist, einen benutzerdefinierten Konstruktor zu definieren, die als Eingabe eine Basisklasse Objekt nehmen:

class TDerived: public TBase { 
public: 
    TDerived(TBase const &other) {} 
}; 

Nachdem alle TDerived und TBase sind verschiedene Klassen, obwohl die erste, die zweite erbt.

+0

Können Sie mir erklären, warum ich keine Ctors erben kann? Siehe auch Update. – Void

3

Falls Sie noch das gleiche Stück Code lesen, heißt es:

// D1 has the following constructors: 
// 1. D1() 
// 2. D1(const D1&) 
// 3. D1(D1&&) 
// 4. D1(int) <- inherited 
}; 

So eine Kopie Ctor noch die Kopie Ctor ist, ist es ein Argument der Klasse TDerived akzeptiert. D1 (int) wird trotzdem automatisch generiert.

Verwandte Themen