web-dev-qa-db-fra.com

Pourquoi le constructeur de copie est-il appelé deux fois dans cet extrait de code?

Je joue avec quelques choses pour comprendre comment fonctionnent les constructeurs de copie. Mais je ne comprends pas pourquoi le constructeur de copie est appelé deux fois pour la création de x2. J'aurais supposé qu'il serait appelé une fois lorsque la valeur de retour de createX() est copiée dans x2.
J'ai également examiné quelques questions connexes sur SO, mais pour autant que je sache, je n'ai pas pu trouver le même scénario simple que celui que je pose ici.

Soit dit en passant, je compile avec -fno-elide-constructors Afin de voir ce qui se passe sans optimisations.

#include <iostream>

struct X {
    int i{2};

    X() {
        std::cout << "default constructor called" << std::endl;
    }

    X(const X& other) {
        std::cout << "copy constructor called" << std::endl;
    }
};

X createX() {
    X x;
    std::cout << "created x on the stack" << std::endl;
    return x;
}

int main() {
    X x1;
    std::cout << "created x1" << std::endl;
    std::cout << "x1: " << x1.i << std::endl << std::endl;    

    X x2 = createX();
    std::cout << "created x2" << std::endl;
    std::cout << "x2: " << x2.i << std::endl;    

    return 0;
}

Voici la sortie:

default constructor called
created x1
x1: 2

default constructor called
created x on the stack
copy constructor called
copy constructor called
created x2
x2: 2

Quelqu'un peut-il m'aider ce que je manque ou néglige ici?

14
c_student

Ce que vous devez retenir ici, c'est que la valeur de retour d'une fonction est un objet distinct. Quand tu fais

return x;

vous copiez initialisez l'objet de valeur de retour avec x. Il s'agit du premier appel de constructeur de copie que vous voyez. ensuite

X x2 = createX();

utilise l'objet retourné pour copier initialize x2 c'est donc la deuxième copie que vous voyez.


Une chose à noter est que

return x;

essaiera de déplacer x dans l'objet de retour s'il le peut. Si vous aviez fait un constructeur de mouvement, vous auriez vu cela s'appeler. La raison en est que les objets locaux étant hors de portée à la fin de la fonction, le compilateur traite l'objet comme une valeur r et ce n'est que s'il ne trouve pas de surcharge valide qu'il revient à le renvoyer comme valeur l.

19
NathanOliver

La première copie est en retour de createX

X createX() {
    X x;
    std::cout << "created x on the stack" << std::endl;
    return x; // First copy
}

La seconde consiste à créer x2 à partir du retour temporaire par createX.

X x2 = createX(); // Second copy

Notez qu'en C++ 17, la deuxième copie est forcée d'être éluée.

12
Jarod42