Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 09/07/2015, à 02:46

Destroyers

[C++] Utiliser les rValue references ? [RESOLU]

Bonjour à tous big_smile

Je ne comprend pas comment obtenir une Rvalue avec un objet ...
j'ai tenté plusieurs choses, mais aucune création d'objet ne passe par le move constructor ...

//class.hpp

class my_class
{
public:
  my_class(int ab, int bb);
  ~my_class();
  my_class(my_class const &other);
  my_class(my_class&& other);
  
  my_class getMe();

  my_class& operator=(const my_class& other);

  int a;
  int *b;
};
my_class operator+(my_class a, my_class b);
//class.cpp

#include "class.hpp"
#include <iostream>


my_class::my_class(int ab, int bb) : a(ab), b(new int(bb))
{
  std::cout<<"Default constructor\n";
}

my_class::my_class(my_class const &other) : a(other.a), b(new int(*other.b))
{
  std::cout<<"Copy constructor\n"; 
}

my_class::my_class(my_class&& other) : a(other.a), b(other.b)
{
  std::cout<<"Move constructor\n";
}

my_class::~my_class()
{
  delete b;
}

my_class my_class::getMe()
{
  return (*this);
}

my_class operator+(my_class a, my_class b)
{
  my_class obj(a.a + b.a, *a.b + *b.b);
  return obj;
}
//main.cpp

#include "class.hpp"
#include <iostream>

inline void print(int const &a)
{
std::cout<<a<<'\n';
}

inline void print(int&& a)
{
std::cout<<a + 1<<'\n';
}

int main()
{
  my_class obj(1,2); 
  my_class obj2(obj); 
  my_class obj3(obj + obj2);
  my_class obj4(my_class(1,2));
  
  //print(2); //affiche 3
  
  return 0;
}

Comment afficher

Move constructor

?????

Merci smile

Dernière modification par Destroyers (Le 12/07/2015, à 16:45)

Hors ligne

#2 Le 09/07/2015, à 06:02

no_spleen

Re : [C++] Utiliser les rValue references ? [RESOLU]

Bonjour,

Tu dois utiliser std::move

my_class obj4(std::move(my_class(1,2)));

Hors ligne

#3 Le 09/07/2015, à 21:41

Destroyers

Re : [C++] Utiliser les rValue references ? [RESOLU]

En effet ça marche merci smile

Cependant il est vraiment impossible d'obtenir une Rvalue objet sans ce std::move ? car my_class(1,2); EST un objet sans nom sans adresse et sans portée (impossible à déréférencer...) alors pourquoi est-ce une Lvalue ??

Hors ligne

#4 Le 09/07/2015, à 22:33

alius

Re : [C++] Utiliser les rValue references ? [RESOLU]

Si c'est possible avec g++ dans le cas où tu retournes par reference une variable que tu a déclaré en local dans la stack d'une fonction par exxemple.
En gros c'est une opti du compilo qui dit "si cet objet va devoir etre détruit en dépilant et que je dois en plus retourner une référence de cet objet alors je le garde en mémoire en faisant appel a my_class::my_class(my_class&& other) ".


typiquement tu peux faire comme ça :

void f(my_class&& p) { cout << "fonction qui utilise une rvalue en argument\n"; }

int main() {

    f(my_class(...));
}

Dernière modification par alius (Le 09/07/2015, à 22:37)


Alius

Hors ligne

#5 Le 10/07/2015, à 00:27

Destroyers

Re : [C++] Utiliser les rValue references ? [RESOLU]

En effet... ça marche. Mais alors si je fais:

my_class objet(my_class(...));

pourquoi est-ce le constructeur par copie (EDIT: c'est celui par défaut qui est appelé) et non celui par mouvement qui est appelé alors qu'on vient de démontrer que my_class(...) était bien une Rvalue ?


Autre question concernant les Rvalue-références.

int& var1 = 2; //impossible
const int& var2 = 2; //possible
int&& var3 = 2; //possible

var1 est impossible sous prétexte que "you cannot use a "mutable" reference because, if you did, you'd be able to modify some object that is about to disappear, and that would be dangerous.".
Dans ce cas, pourquoi var3 est-il possible alors que var3 se réfère toujours à une valeur sur le point de disparaître ? Comment est il possible de pouvoir modifier une Rvalue ?

Autre chose: comment var2 peut elle se référer à une Rvalue... le principe d'une référence est d'avoir la même adresse mémoire que ce vers quoi elle réfère... or "2" n'a pas d'adresse, c'est le principe d'une Rvalue. Alors var2 réfère sur quoi en réalité ? comment var2 prend elle une adresse en mémoire ?
Même question pour var3, c'est une Rvalue-référence, mais comment prend-elle une adresse en mémoire alors que "2" n'en a pas ?

Merci d'avance smile

Dernière modification par Destroyers (Le 10/07/2015, à 14:08)

Hors ligne

#6 Le 10/07/2015, à 07:32

no_spleen

Re : [C++] Utiliser les rValue references ? [RESOLU]

Bonjour,

Je pense qu'il s'agit simplement d'une optimisation du compilateur.

Si tu regardes les displays obtenus par la ligne suivante

my_class obj4{my_class(1,2)};

Tu n'en a qu'un, qui est le constructeur par défaut. -> Le compilateur est assez malin pour comprendre qu'il n'a pas besoin de créer l'object et d'ensuite le déplacer.

Sans optimisation, tu devrais en avoir 2, un display pour la création par défaut du my_class(1,2), et un contructeur par R-value.

En utilisant std:move, tu forces le comportement voulu, qui est en fait moins performant.

Hors ligne

#7 Le 10/07/2015, à 13:57

Destroyers

Re : [C++] Utiliser les rValue references ? [RESOLU]

Ah... Merci, ça aide à comprendre bien des choses smile

Mais alors une nouvelle question se pose... A quoi sert cette move semantic si le compilateur ne nous fait jamais passer par le constructeur par mouvement ? ...
J'ai écrit les opérateurs (=) :

my_class& my_class::operator=(const my_class& other)
{
  a = other.a;
  delete b;
  b = new int(*other.b);
  
  std::cout<<"Assignment operator\n";
  return *this;
}

my_class& my_class::operator=(my_class&& other)
{
  a = other.a;
  b = other.b;
  other.b = nullptr;
  
  std::cout<<"Move operator\n";
  return *this;
}

et là, paradoxalement, si je fais :

my_class obj(1,2); 
my_class obj2(obj);
std::cout<<'\n';
obj = obj2;
std::cout<<'\n';
obj = my_class(1,2);

Alors là en sortie j'obtiens bien:

Default constructor
Copy constructor

Assignment operator

Default constructor
Move operator

Donc ici mon objet temporaire est bien construit (Default constructor) et passe bien dans le move operator !

Du coup je me pose la question suivante : le CONSTRUCTEUR par mouvement, serait-il tout simplement inutile ? Si le compilateur juge plus efficace d'utiliser le constructeur de par défaut sans construire la Rvalue, j'en conclue que la réponse est oui ..

Hors ligne

#8 Le 10/07/2015, à 22:25

grigouille

Re : [C++] Utiliser les rValue references ? [RESOLU]

compile avec -fno-elide-constructors

$ cat toto.cpp
#include <iostream>
using namespace std;

class Toto
{
	int *p;
public:
	~Toto(){delete p;cout << "destructor\n";}
	Toto(int j = {}) : p{new int{j}} {cout << "constructor\n";}
	Toto(const Toto&a ):p{new int{a.p ? *a.p : 0}} {cout << "copy constructor\n";}
	Toto(Toto&& a):p{a.p} {a.p = nullptr;cout << "move constructor\n";}

	Toto& operator=(const Toto& a) {*p = *a.p;cout << "assignement\n";return *this;}
	Toto& operator=(Toto&& a) {if(this != &a) {p = a.p;a.p=nullptr;}cout << "move assignement\n";return *this;}
};

Toto f()
{
	return Toto();
}


int main()
{
	const Toto t{f()};
	return 0;

}
$ g++ -o toto -fno-elide-constructors -std=c++11 toto.cpp && ./toto
constructor
move constructor
destructor
move constructor
destructor
destructor
$ g++ -o toto -std=c++11 toto.cpp && ./toto
constructor
destructor

Debian (xfce) 12
HP LaserJet M1132 MFP

Hors ligne

#9 Le 12/07/2015, à 13:11

alius

Re : [C++] Utiliser les rValue references ? [RESOLU]

int& var1 = 2; //impossible
const int& var2 = 2; //possible
int&& var3 = 2; //possible

La première ligne ne compile pour la raison que te donne le compilateur, 2 est une constante (temporaire) donc il faut une référence à un int constant. En fait le compilateur alloue automatiquement de la mémoire pour les  temporaires comme les constantes 2, 0.5f ou encore "bonjour". Donc elles ont belle et bien une adresse !
Lorsque tu déclare intt&& var3 = 2 tu dit au compilo de faire un move sur la constante temporaire 2 qui de ce fait n'en ai plus une.


Alius

Hors ligne

#10 Le 12/07/2015, à 16:44

Destroyers

Re : [C++] Utiliser les rValue references ? [RESOLU]

Okéééé tout est plus claire smile
Merci à tous.

Du coup je doute quand même qu'utiliser la Move sementic ait une utilité quelconque puisque g++ optimise de telle sorte que les rvalue de passent pas par le move constructor mais par le constructeur "par défaut" (c'est pas celui par défaut... voir mes exemples, mais c'est pas le move constructor). Et le fait de forcer g++ a le faire avec -fno-elide-constructors est sûrement une perte d'optimisation.
Je n'utiliserai pas la move sémentic du coup

Merci encore.

Hors ligne

#11 Le 13/07/2015, à 11:47

grim7reaper

Re : [C++] Utiliser les rValue references ? [RESOLU]

Destroyers a écrit :

Du coup je me pose la question suivante : le CONSTRUCTEUR par mouvement, serait-il tout simplement inutile ? Si le compilateur juge plus efficace d'utiliser le constructeur de par défaut sans construire la Rvalue, j'en conclue que la réponse est oui ..

Si, ça a son usage (cf. cette réponse sur SO + la réponse dét aillée eventuellement).
Un autre truc à savoir c’est que le compilateur peut faire de la RVO ou de la NRVO (ces optimisations existaient avant C++11) et que dans ces cas-là aucun constructeur n’est appelé.

Hors ligne

#12 Le 13/07/2015, à 16:56

Destroyers

Re : [C++] Utiliser les rValue references ? [RESOLU]

Mouais, les cas où ces optimisations (RVO et NRVO) ne sont pas appliquées sont exceptionnels.
Pour qu’elle ne s’applique pas, il faut plusieurs points de sortie ET des objets nommés.
Et c'est selon mes expériences le seul moyen d'appeler un move constructor... (hormis utiliser std::move ou l'option -fno-elide-constructors qui sont alors des pertes d'optimisation)

C'est quand même très restreint selon moi hmm

Hors ligne

#13 Le 13/07/2015, à 20:23

grim7reaper

Re : [C++] Utiliser les rValue references ? [RESOLU]

En même, le but principal de la move semantic ce n’est pas l’optimisation (ça s’appelle move semantic, pas move optimisation wink).
C’est surtout utile pour le concept de propriété (ownership, concept très important, et central en Rust par exemple) que l’on rencontre avec les objets non-copiable (std::unique_ptr entre autres) par exemple.

Hors ligne