4

J'ai eu des difficultés à comprendre les constructeurs de déplacement en C++. J'ai fait une classe simple avec un constructeur par défaut, un constructeur de copie, un constructeur de déplacement et un destructeur. Aussi, j'ai défini une fonction avec deux surcharges, on accepte une référence à cette classe et on accepte une référence rvalue à cette classe. Mon code de test est ci-dessous.Confusion avec les constructeurs de déplacement: impossible d'appeler le constructeur de déplacement

#include <iostream> 


class c { 

public: 

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

    c(const c& s) { 
     std::cout << "copy constructor" << std::endl; 
    } 

    c(c&& s) { 
     std::cout << "move constructor" << std::endl; 
    } 

    ~c() { 
     std::cout << "destructor" << std::endl; 
    } 

}; 

void f(c& s) { 
    std::cout << "passed by reference" << std::endl; 
} 

void f(c&& s) { 
    std::cout << "passed by rvalue reference" << std::endl; 
} 

int main() { 

    c s1; // line 1 
    std::cout << "\n"; 
    c s2(s1); // line 2 
    std::cout << "\n"; 
    c s3(c()); // line 3 

    std::cout << "\n"; 

    f(s1); // line 4 
    std::cout << "\n"; 
    f(c()); // line 5 

    getchar(); 
    return 0; 

} 

La sortie que j'obtiens n'est pas ce à quoi je m'attendais. Voici la sortie que je reçois de ce code.

default constructor 

copy constructor 

passed by reference 

default constructor 
passed by rvalue reference 
destructor 

Je peux comprendre les sorties de toutes les lignes sauf line 3. Sur line 3, qui est c s3(c());, c() est un rvalue donc je m'attendrais à ce que s3 soit déplacé construit. Mais la sortie ne montre pas qu'elle est construite. Sur line 5, je fais la même chose et en passant un rvalue à la fonction f() et il appelle en effet la surcharge qui accepte une référence rvalue. Je suis très confus et apprécierais toute information à ce sujet.

Edit: Je suis en mesure d'appeler le constructeur de déplacement si je c s3(std::move(c())); mais suis-je pas déjà passer un rvalue à s3? Pourquoi aurais-je besoin de std::move?

+2

@NathanOliver Ce n'est pas vraiment un double de celui-ci. Ce qui se passe ici est l'analyse la plus vexante, et non l'élision. – Angew

+0

@Angew Bon appel. Manqué ça. – NathanOliver

+0

@ Angew Ce que j'essayais réellement de faire était de construire un objet à travers son constructeur de mouvement et quand NathanOliver m'a dirigé vers l'autre question, j'ai vraiment pensé que c'était un doublon. Je n'ai même pas remarqué que la déclaration à la ligne 3 était une signature de fonction jusqu'à ce que vous l'indiquiez. Je n'ai jamais entendu parler de la plus vexante analyse, alors merci de partager cela avec moi; Je vais le lire pour apprendre ce que c'est. – Deniz

Répondre

9

La raison pour laquelle vous ne voyez aucune sortie de la ligne 3 est qu'elle déclare une fonction, pas une variable. Cela est dû à une ambiguïté appelée Most Vexing Parse.

Comparer c s3(c()) avec int foo(int()), ce qui, grâce aux ajustements de type implicites, est identique à int foo(int (*f)()).

Pour contourner ce problème, utilisez l'initialisation du croisillon (qui a été effectivement introduit en C++ 11 en partie pour cette raison):

c s3(c{}); 

// or 

c s3{c()}; 

// or 

c s3{c{}}; 
+0

Merci pour votre réponse informative. Tant ceci que la réponse de M.M ci-dessous répondent à ma question. Cependant, apparemment, vous étiez une minute plus tôt qu'eux, alors j'accepte votre réponse. Merci à M.M pour votre temps et votre aide. – Deniz

5

c s3(c()); ne construit aucun objet. C'est une déclaration de fonction. La fonction est appelée s3, le type de retour est c et le type de paramètre est "pointeur vers fonction ne prenant pas de paramètre et renvoyant c". Vous n'obtenez donc aucune sortie, car une déclaration de fonction n'appelle aucune fonction.

Pour éviter ce genre de chose, vous pouvez utiliser l'initialisation de la liste, c s3{c()}; est probablement plus en ligne avec ce que vous aviez en tête.

+0

+1 pour info 'le type de paramètre est "pointeur vers fonction ne prenant aucun paramètre et renvoyant c"'. si c'était 'c (x)' alors le type de paramètre est 'c' et le nom est' x' –