C ++ 17 Update
In C ++ 17 wurde die Bedeutung von A_factory_func()
von der Erstellung eines temporären Objekts (C ++ <= 14) geändert, um lediglich die Initialisierung des Objekts anzugeben, für das dieser Ausdruck in C ++ 17 (lose gesagt) initialisiert wird. Diese Objekte (als "Ergebnisobjekte" bezeichnet) sind die Variablen, die durch eine Deklaration (wie a1
) erstellt wurden, künstliche Objekte, die erstellt wurden, wenn die Initialisierung verworfen wurde, oder wenn ein Objekt für die Referenzbindung benötigt wird (wie z A_factory_func();
. B. in . Im letzten Fall Ein Objekt wird künstlich erstellt, was als "temporäre Materialisierung" bezeichnet wird, da A_factory_func()
es keine Variable oder Referenz gibt, für deren Existenz sonst ein Objekt erforderlich wäre.
Als Beispiele in unserem Fall besagen im Fall von a1
und a2
Sonderregeln, dass in solchen Deklarationen das Ergebnisobjekt eines prvalue-Initialisierers vom gleichen Typ wie a1
variabel a1
ist und daher A_factory_func()
das Objekt direkt initialisiert a1
. Eine Zwischenbesetzung im funktionalen Stil hätte keine Auswirkung, da A_factory_func(another-prvalue)
nur das Ergebnisobjekt des äußeren Wertes "durchlaufen" wird, um auch das Ergebnisobjekt des inneren Wertes zu sein.
A a1 = A_factory_func();
A a2(A_factory_func());
Hängt davon ab, welcher Typ A_factory_func()
zurückgibt. Ich gehe davon aus, dass es ein zurückgibt A
- dann macht es dasselbe - außer dass, wenn der Kopierkonstruktor explizit ist, der erste fehlschlägt. Lesen Sie 8.6 / 14
double b1 = 0.5;
double b2(0.5);
Dies geschieht genauso, da es sich um einen integrierten Typ handelt (dies bedeutet hier keinen Klassentyp). Lesen Sie 8.6 / 14 .
A c1;
A c2 = A();
A c3(A());
Dies ist nicht dasselbe. Der erste Standard initialisiert, wenn A
es sich um einen Nicht-POD handelt, und führt keine Initialisierung für einen POD durch (Lesen Sie 8.6 / 9 ). Die zweite Kopie wird initialisiert: Der Wert initialisiert eine temporäre Kopie und kopiert diesen Wert dann in c2
(Lesen Sie 5.2.3 / 2 und 8.6 / 14 ). Dies erfordert natürlich einen nicht expliziten Kopierkonstruktor (Lesen Sie 8.6 / 14 und 12.3.1 / 3 und 13.3.1.3/1 ). Die dritte Methode erstellt eine Funktionsdeklaration für eine Funktion c3
, die eine zurückgibt A
und einen Funktionszeiger auf eine Funktion zurückgibt , die a zurückgibt A
(Read 8.2 ).
Eintauchen in Initialisierungen Direkt- und Kopierinitialisierung
Während sie identisch aussehen und dasselbe tun sollen, unterscheiden sich diese beiden Formen in bestimmten Fällen erheblich. Die beiden Formen der Initialisierung sind Direkt- und Kopierinitialisierung:
T t(x);
T t = x;
Es gibt ein Verhalten, das wir jedem von ihnen zuschreiben können:
- Die direkte Initialisierung verhält sich wie ein Funktionsaufruf einer überladenen Funktion: Die Funktionen sind in diesem Fall die Konstruktoren von
T
(einschließlich explicit
derjenigen), und das Argument lautet x
. Die Überlastungsauflösung findet den am besten passenden Konstruktor und führt bei Bedarf alle erforderlichen impliziten Konvertierungen durch.
- Die Kopierinitialisierung erstellt eine implizite Konvertierungssequenz: Sie versucht,
x
in ein Objekt vom Typ zu konvertieren T
. (Es kann dann über dieses Objekt in das zu initialisierende Objekt kopiert werden, sodass auch ein Kopierkonstruktor benötigt wird - dies ist jedoch unten nicht wichtig.)
Wie Sie sehen, ist die Kopierinitialisierung in gewisser Weise Teil der direkten Initialisierung im Hinblick auf mögliche implizite Konvertierungen: Während bei der direkten Initialisierung alle Konstruktoren zum Aufrufen verfügbar sind und darüber hinaus jede implizite Konvertierung durchgeführt werden kann, die zum Abgleichen der Argumenttypen erforderlich ist, wird die Kopierinitialisierung durchgeführt kann nur eine implizite Konvertierungssequenz einrichten.
Ich habe mich sehr bemüht und den folgenden Code erhalten, um für jedes dieser Formulare einen anderen Text auszugeben , ohne das "Offensichtliche" durch explicit
Konstruktoren zu verwenden.
#include <iostream>
struct B;
struct A {
operator B();
};
struct B {
B() { }
B(A const&) { std::cout << "<direct> "; }
};
A::operator B() { std::cout << "<copy> "; return B(); }
int main() {
A a;
B b1(a); // 1)
B b2 = a; // 2)
}
// output: <direct> <copy>
Wie funktioniert es und warum gibt es dieses Ergebnis aus?
Direkte Initialisierung
Es weiß zunächst nichts über Konvertierung. Es wird nur versucht, einen Konstruktor aufzurufen. In diesem Fall ist der folgende Konstruktor verfügbar und stimmt genau überein :
B(A const&)
Es ist keine Konvertierung erforderlich, geschweige denn eine benutzerdefinierte Konvertierung, die zum Aufrufen dieses Konstruktors erforderlich ist (beachten Sie, dass auch hier keine Konvertierung der Konstantenqualifizierung stattfindet). Die direkte Initialisierung nennt es also.
Initialisierung kopieren
Wie oben erwähnt, erstellt die Kopierinitialisierung eine Konvertierungssequenz, wenn a
sie nicht typisiert B
oder davon abgeleitet wurde (was hier eindeutig der Fall ist). Es wird also nach Möglichkeiten suchen, die Konvertierung durchzuführen, und die folgenden Kandidaten finden
B(A const&)
operator B(A&);
Beachten Sie, wie ich die Konvertierungsfunktion umgeschrieben habe: Der Parametertyp spiegelt den Typ des this
Zeigers wider , der in einer Nicht-Konstanten-Elementfunktion auf Nicht-Konstante steht. Nun nennen wir diese Kandidaten x
als Argument. Der Gewinner ist die Konvertierungsfunktion: Wenn wir zwei Kandidatenfunktionen haben, die beide einen Verweis auf denselben Typ akzeptieren, gewinnt die Version mit weniger Konstanten (dies ist übrigens auch der Mechanismus, der Nicht-Konstanten-Mitgliedsfunktionen bevorzugt, ruft Nicht auf -const Objekte).
Beachten Sie, dass die Konvertierung mehrdeutig ist, wenn wir die Konvertierungsfunktion in eine const-Member-Funktion ändern (da beide einen Parametertyp von A const&
then haben): Der Comeau-Compiler lehnt sie ordnungsgemäß ab, GCC akzeptiert sie jedoch im nicht pedantischen Modus. Durch Umschalten auf -pedantic
wird jedoch auch die richtige Mehrdeutigkeitswarnung ausgegeben.
Ich hoffe, dies hilft etwas, um klarer zu machen, wie sich diese beiden Formen unterscheiden!
A c1; A c2 = c1; A c3(c1);
.