8

den folgenden Code vor:Copy-Konstruktor nicht aufgerufen, wenn ein Objekt mit Rückgabewert einer Funktion initialisiert

#include <iostream> 

using namespace std; 

class A 
{ 
    public: 
     int a; 
     A(): a(5) 
     { 
      cout << "Constructor\n"; 
     } 
     A(const A &b) 
     { 
      a = b.a; 
      cout << "Copy Constructor\n"; 
     } 
     A fun(A a) 
     { 
      return a; 
     } 
}; 

int main() 
{ 
    A a, c; 
    A b = a.fun(c); 
    return 0; 
} 

Die Ausgabe des obigen Codes mit g++ file.cpp ist:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 

Der Ausgang des obige Code mit g++ -fno-elide-constructors file.cpp ist:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 
Copy Constructor 

ich weiß Rückgabewert Optimiz aktion. Meine Frage ist, welcher Aufruf zum Kopieren Konstruktor ist elided (temporäres Objekt während der Rückgabe oder zurückgegebenes Objekt kopiert nach b)?

Wenn der elided Copy-Konstruktor derjenige ist, der zum Erstellen von b verwendet wird, wie wird b überhaupt erstellt (weil es in diesem Fall auch keinen Konstruktoraufruf gibt)?

Wenn ich die Zeile A b = a.fun(c); durch a.fun(c) ersetze und mit der ersten Methode oder sogar der zweiten Methode kompiliere, dann wird auch der Kopierkonstruktor 2 mal aufgerufen. Wenn also in dem im vorherigen Absatz erläuterten Fall der Kopierkonstruktor des temporären Objekts entfernt wird, warum wird er dann nicht entfernt?

+0

Wie ich Ich habe dieses Zeug untersucht, als ich davon lernte. Alles war 'std :: cout <<" Konstruktor kopieren: "<< (void *) b <<" to "<< (void *) Dieses << std :: endl;' und 'std :: cout <<" Konstruieren "<< (void *) dieses << std :: endl' Bonuspunkte, wenn Sie Züge in C++ 11 hinzufügen. – IdeaHat

Antwort

6
#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    int a; 
    A(): a(5) 
    { 
     cout << "Constructing: " << (void *)this << std::endl; 
    } 
    A(const A &b) 
    { 
     a = b.a; 
     cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl; 
    } 
    A fun(A a) 
    { 
     return a; 
    } 
}; 

int main() 
{ 

    A a, c; 
    A b = a.fun(c); 

    std::cout << "a:" << (void *)&a << std::endl << 
       "b:" << (void *)&b << std::endl << 
       "c:" << (void *)&c << std::endl; 
    return 0; 
} 

Ausbeuten:

Constructing: 0x7fffbb377220 
Constructing: 0x7fffbb377210 
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210 
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230 
a:0x7fffbb377220 
b:0x7fffbb377200 
c:0x7fffbb377210 

es so konstruiert a konstruiert c, Kopien c zu einem Zwischen (Argument a der Funktion), und dann Kopiert das Intermediat direkt in b und überspringt das typische Kopieren von a nach return mittlere. Dies wird auch gezeigt, besser, wenn Sie nach Wert (Änderung A fun(const A& a) passieren:

Constructing: 0x7fff8e9642b0 
Constructing: 0x7fff8e9642a0 
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0 
a:0x7fff8e9642b0 
b:0x7fff8e964290 
c:0x7fff8e9642a0 

a ausgebildet ist, c aufgebaut ist, wird direkt b c kopiert, trotz b nicht zu Spaß weitergegeben werden

4

Die kopierte Kopie ist die Kopie des temporären Rückgabewerts in b. Ohne Elision wird der Rückgabewert von a initialisiert und nach b kopiert. Stattdessen wird das Temporär, das ansonsten den Rückgabewert enthalten würde, in b umgewandelt und mit a initialisiert. [Class.copy]/31:

wenn ein temporäres Klassenobjekt, das mit derselben gebunden ist/ cv-unqualifizierten Typ in ein Klassenobjekt bewegte kopiert werden würde, zu einer Referenz (12.2) nicht, die kopieren/verschieben Betrieb kann durch Aufbau des temporären Objekt direkt in das Ziel des weggelassen kopieren/verschieben

Sie verzichtet werden kann dies beobachten, wenn Sie einen zusätzlichen Ausgang in fun hinzufügen:

A fun(A a) 
{ 
    cout << "fun!" << endl; 
    return a; 
} 

Dann mit dem elision werden Sie

[...]
Spaß bekommen!
Copykonstruktor

Und ohne:

[...]
Spaß!
Copykonstruktor
Copykonstruktor