Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    août 2015
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : août 2015
    Messages : 6
    Points : 5
    Points
    5

    Par défaut Surcharge opérateur +, création d'une variable intermédiaire sans raison !?

    Bonjour,

    j'essaie de comprendre le fonctionnement de la surcharge d'opérateur dans le but d'optimiser la passation de paramètres et ainsi éviter la création de variables intermédiaires inutiles.

    J'ai créé une classe Test et surchargé l'opérateur + en interne et en externe, tant qu'à faire :-)

    Il me semble avoir compris le fonctionnement de la surcharge interne. Par contre avec la surcharge externe, il y a un appel supplémentaire au ctor que je ne comprends pas !!

    Ci-dessous le code du programme, ainsi que les sorties console en interne et externe.
    Dans la sortie externe la ligne incriminée est en gras.

    Je m'étais à un moment demandé si cela ne venaient pas des valeurs de retour, car les parties de code concernant les surcharges de l'opérateur + interne/externe ne renvoient pas des références !!
    Mais alors pourquoi le ctor n'est appelé que pour la surcharge externe !?


    Merci par avance à tous ceux ou celles qui passeront du temps sur mon problème.
    mokaude


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
     
    #include <iostream>
    #include <string>
    using namespace std;
     
     
    // utilisation de la méthode
    #define INTERN 	0
    #define EXTERN 	1
     
    /*
    	g++ -std=c++14 dev_surcharge.cpp -D OP_PLUS_EXT=0 -o dev_surcharge	// INTERN
    	g++ -std=c++14 dev_surcharge.cpp -D OP_PLUS_EXT=1 -o dev_surcharge	// EXTERN
    */
     
     
     
    // #define OP_PLUS_EXT		BOTH
    // #define BOTH	7	// BOTH les deux !! (test pour connaître le choix du compilo)
    // 					error: use of overloaded operator '+' is ambiguous
    // 					     (with operand types 'Test' and 'Test')
     
     
     
    class Test {
    private:
        int val;
        string name;
     
    public:
    	Test (string s="default", int v=-1);
    	Test (Test const&);
    	~Test ();
     
    	ostream& affiche (ostream& sortie) const;
     
    // surcharge interne
    	Test& operator+= (Test const&);
    	Test& operator= (Test const&);
     
    #if OP_PLUS_EXT == INTERN
    	Test const operator+ (Test const&);
    #endif
    };
     
     
    ostream& operator<<(ostream&, Test const&);
     
     
     
     
    Test::Test (string s, int v)
    //
    : name (s), val (v)
    {
    	cout << "NEW val : " << *this << endl;
    }
     
     
     
    Test::Test (Test const& t)
    : name( t.name+"_dup"), val( t.val)
    {
    	cout << "DUP val : " << t << endl;
    }
     
     
     
     
    Test::~Test ()
    {
    	cout << "DEL val : " << name << " " << val << endl;
    }
     
     
     
     
    Test& Test::operator= (Test const& t)
    {
    	cout << "OP = : " << *this << " = " << t << endl;
     
    	name = name +"="+t.name;
    	val = t.val;
     
    	return *this;
    }
     
     
     
     
     
    Test& Test::operator+= (Test const& t)
    {
    	cout << "OP+= : " << *this << " += " << t << endl;
     
    	val += t.val;
    	name = name+"+="+t.name;
     
    	cout << "OP+= END : " << *this << endl;
     
    	return *this;	// utile  pour affectation à la suite : t3 = (t1+=t2);
    					// renvoie alors &t1 pour affectation dans t3
    					// pas la peine de créer une copie locale car t1 existe déjà
    }
     
     
     
     
     
     
    #if OP_PLUS_EXT == EXTERN
    Test const operator+(Test, Test const&);
     
    const Test operator+ (Test t1, Test const& t2)
    {
    	cout << "OP+ EXT : " << t1 << t2 << endl;
     
    	t1 += t2; // passage par une méthode -> interne
    	return t1;
    }
     
    #else
    Test const Test::operator+ (Test const& t)
    {
    	cout << "OP+ INT : " << *this << t << endl; // ERROR ???
     
    	Test tmp;
    	tmp.val = val + t.val;
    	tmp.name = name+"+"+t.name;
    	return tmp;
    }
    #endif
     
     
     
     
    ostream& Test::affiche(ostream& sortie) const
    {
      sortie << "(" << name << " : " << val << ")";
      return sortie;
    }
     
     
    ostream& operator<<(ostream& sortie, Test const& t)
    {
      return t.affiche( sortie);
    }
     
     
     
     
    // /*
    // 	ces deux fonctions pour vérifier que cout << t; 
    // 	s'excute sans erreur dans une fonction
    // */
    // void display_ref(Test &);
    // void display_ref(Test &t){
    // 	cout << "display ref " << t << endl;
    // }
    // 
    // void display_val(Test);
    // void display_val(Test t){
    // 	cout << "display val " << t << endl;
    // }
     
     
     
     
    int main()
    {
    	Test t1("t1",1);
    	Test t2("t2",2);
    	cout << t1 << t2 << endl;
     
    	// Test t3(t2);
    	// cout << t3 << endl;
     
    	// cout << "display_ref" << endl;
    	// display_ref(t1);
    	// cout << "display_val" << endl;
    	// display_val(t1);
    	// cout << "end display" <<  endl;
     
     
    	cout << "TEST1 + " << endl;
    	cout << "** " << (t1+t2) << " **" << endl;
     
    	// cout << "TEST2 + " << endl;
    	// Test t4("t4", 16);
    	// cout << t4 << endl;
    	// cout << "** " << (t4 = (t1+t2)) << " **" << endl;
    	// cout << t4 << endl;
     
     
     
    	// cout << "TEST1 =" << endl; // OK
    	// Test t5("t5", 5);
    	// // t5 += t1;
    	// cout << t5 << endl;
     
    	// t5 = t1;
    	// cout << t5 << endl;
     
    	// cout << (t5 = t2) << endl;
     
    	// cout << "TEST2 +=" << endl;
    	// t4 = {"aze", 13};
    	// cout << t4 << endl;
     
    	cout << "END" << endl;
     
        return 0;
    }


    Surcharge interne : OK
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    [cpp-poo/ztest]$g++ -std=c++14 dev_surcharge.cpp -D OP_PLUS_EXT=0 -o dev_surcharge
    [cpp-poo/ztest]$ ./dev_surcharge
    NEW val : (t1 : 1)
    NEW val : (t2 : 2)
    (t1 : 1)(t2 : 2)
    TEST1 +
    ** OP+ INT : (t1 : 1)(t2 : 2)
    NEW val : (default : -1)
    (t1+t2 : 3) **
    DEL val : t1+t2 3
    END
    DEL val : t2 2
    DEL val : t1 1

    Surcharge externe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [cpp-poo/ztest]$ g++ -std=c++14 dev_surcharge.cpp -D OP_PLUS_EXT=1 -o dev_surcharge
    [cpp-poo/ztest]$ ./dev_surcharge
    NEW val : (t1 : 1)
    NEW val : (t2 : 2)
    (t1 : 1)(t2 : 2)
    TEST1 +
    ** DUP val : (t1 : 1)
    OP+ EXT : (t1_dup : 1)(t2 : 2)
    OP+= : (t1_dup : 1) += (t2 : 2)
    OP+= END : (t1_dup+=t2 : 3)
    DUP val : (t1_dup+=t2 : 3)
    (t1_dup+=t2_dup : 3) **
    DEL val : t1_dup+=t2_dup 3
    DEL val : t1_dup+=t2 3
    END
    DEL val : t2 2
    DEL val : t1 1
    Fichiers attachés Fichiers attachés

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    809
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 809
    Points : 4 209
    Points
    4 209

    Par défaut

    Bonjour,

    Attention ton opérateur interne doit être déclaré const (car + ne modifie pas l'objet).
    Et la valeur retournée doit être non const pour être sûr d'une optimisation correcte.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	Test Test::operator+( Test const& ) const
    L'interne est totalement équivalent (du moins dans ce qui va se dérouler) à une fonction qui ajoute un premier paramètre de type Test const&.
    Il y a trois chose à mesurer :
    1) ce qu'il faut faire pour passer les 2 paramètres (a priori une référence constante est mieux qu'une copie mais pas toujours)
    2) ce qui se passe dans le corps de la fonction, si on peut éviter de créer un objet c'est pas plus mal.
    3) ce qui se passe au moment du return jusqu'à l'arrivée dans une lvalue ou une rvalue résultat.
    Le compilateur va pouvoir éliminer des copies surtout pendant les phases (1) et (3), il peut être perturbé si les opérations de copie ont été redéfinies ce qui est ton cas.
    L'externe que tu présentes semble un peu plus optimum que ton interne, mais cela dépend du contexte.

    Il est donc difficile de conclure car cela dépend des optimisations et surtout si les paramètres et le résultat sont des lvalues ou des rvalues.
    Personnellement j'écrirai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Test Test::operator+( Test t )const {
    	return  t += *this;       // profitons de la commutativité de l'addition
    }
    // ou en externe
    Test operator+( Test t1 , Test const& t2 ) {
    	return  t1 += t2;
    }

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    août 2015
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : août 2015
    Messages : 6
    Points : 5
    Points
    5

    Par défaut

    Merci pour cette réponse, même si pour l'instant quelques points me dépassent un peu... mais ce n'est pas grave, car ça me donne matière à creuser davantage cette question ;-))


    Citation Envoyé par dalfab Voir le message
    Bonjour,

    Attention ton opérateur interne doit être déclaré const (car + ne modifie pas l'objet).
    Et la valeur retournée doit être non const pour être sûr d'une optimisation correcte.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	Test Test::operator+( Test const& ) const
    D'accord sur ce point de déclaration const pour l'opérateur interne.
    Par contre pourquoi dans le code du haut, l'argument est une référence const et pas dans celui du bas ? Dans ce cas, comment retourner le résultat de la somme ? Et où est stockée la somme obtenue ?


    Citation Envoyé par dalfab Voir le message
    L'interne est totalement équivalent (du moins dans ce qui va se dérouler) à une fonction qui ajoute un premier paramètre de type Test const&.
    Si this est considéré/déclaré comme une référence const, comment une méthode pourrait alors modifier l'instance courante ? Ou alors, n'est-ce que dans mon cas précis ?


    Citation Envoyé par dalfab Voir le message
    Il y a trois chose à mesurer :
    1) ce qu'il faut faire pour passer les 2 paramètres (a priori une référence constante est mieux qu'une copie mais pas toujours)
    2) ce qui se passe dans le corps de la fonction, si on peut éviter de créer un objet c'est pas plus mal.
    3) ce qui se passe au moment du return jusqu'à l'arrivée dans une lvalue ou une rvalue résultat.
    Le compilateur va pouvoir éliminer des copies surtout pendant les phases (1) et (3), il peut être perturbé si les opérations de copie ont été redéfinies ce qui est ton cas.
    L'externe que tu présentes semble un peu plus optimum que ton interne, mais cela dépend du contexte.
    Concernant l'opération de copie que j'ai redéfinie, est-ce par obligation car je travaille sur une classe et non un type de base ?

    Citation Envoyé par dalfab Voir le message
    Il est donc difficile de conclure car cela dépend des optimisations et surtout si les paramètres et le résultat sont des lvalues ou des rvalues.
    Personnellement j'écrirai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Test Test::operator+( Test t )const {
    	return  t += *this;       // profitons de la commutativité de l'addition
    }
    // ou en externe
    Test operator+( Test t1 , Test const& t2 ) {
    	return  t1 += t2;
    }
    Je comprends bien que selon l'utilisation de la fonction, les optimisations ne puissent pas être établies de la même façon. Mais une somme ne pourra jamais être une lvalue ? C'est bien çà ? Ce qui fait que personnellement, je l'aurais déclaré le retour en const.

    Sinon, concernant la surcharge interne, ne pas déclarer le paramètre t en référence reviendrait à travailler sur une copie modifiable de t pour y stocker le résultat de la somme ?

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    809
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 809
    Points : 4 209
    Points
    4 209

    Par défaut

    Citation Envoyé par mokaude Voir le message
    Par contre pourquoi dans le code du haut, l'argument est une référence const et pas dans celui du bas ?
    Vu de l'appelant, une const référence et un passage par copie sont totalement équivalents, l'objet de l'appelant est inchangé. Mais pour la fonction elle-même c'est différent, on peut donc choisir ce que l'on veut et donc ici la meilleure optimisation.
    Citation Envoyé par mokaude Voir le message
    Dans ce cas, comment retourner le résultat de la somme ? Et où est stockée la somme obtenue ?
    Le résultat est la copie de l'expression dans le return, le type de retour non référence correspond toujours à une copie (parfois éludée par optimisation.)
    Citation Envoyé par mokaude Voir le message
    Si this est considéré/déclaré comme une référence const, comment une méthode pourrait alors modifier l'instance courante ? Ou alors, n'est-ce que dans mon cas précis ?
    Justement quand on fait A+B on veut obtenir un objet résultat mais A et B ne doivent pas être modifiés. L'instance courante ne doit surtout pas être modifiée.
    Citation Envoyé par mokaude Voir le message
    Concernant l'opération de copie que j'ai redéfinie, est-ce par obligation car je travaille sur une classe et non un type de base ?
    On ne doit normalement jamais définir l'opérateur et le constructeur par copie, le compilateur sait le faire à ta place. Il ne faut le faire que pour gérer des problèmes particuliers de ressources ou d'allocation mémoire. Voir la règle des 5.
    Citation Envoyé par mokaude Voir le message
    Je comprends bien que selon l'utilisation de la fonction, les optimisations ne puissent pas être établies de la même façon. Mais une somme ne pourra jamais être une lvalue ? C'est bien çà ? Ce qui fait que personnellement, je l'aurais déclaré le retour en const.
    En effet, une somme est toujours une rvalue, mais si on l'affecte immédiatement à une lvalue, il y aura optimisation NRVO. Pourquoi const? on fait ce qu'on veut du résultat pourquoi empêcher les modifications?
    si on écrit A + B + C, c'est calculé operator+( operator+(A,B) , C ), si operator+(A,B) retourne un objet constant, quand on ajoute C, on doit faire operator+( rvalue_constante , C ), et si le premier paramètre attendu est une copie, il y aura copie pour ne pas modifier la constante. Si la rvalue n'est pas constante elle est directement réutilisée comme étant la copie.
    Citation Envoyé par mokaude Voir le message
    Sinon, concernant la surcharge interne, ne pas déclarer le paramètre t en référence reviendrait à travailler sur une copie modifiable de t pour y stocker le résultat de la somme ?
    Oui, ça devient un objet local, on en fait ce que l'on veut donc on peut le modifier et sans servir dans le return.

  5. #5
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 480
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 5 480
    Points : 23 813
    Points
    23 813

    Par défaut

    Pour savoir comment déclarer les opérateurs : https://en.wikipedia.org/wiki/Operat..._C_and_C%2B%2B
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    août 2015
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : août 2015
    Messages : 6
    Points : 5
    Points
    5

    Par défaut

    Merci, je ne connaissais pas. Ça me sera bien utile ;-)

    Citation Envoyé par dalfab Voir le message
    Vu de l'appelant, une const référence et un passage par copie sont totalement équivalents, l'objet de l'appelant est inchangé. Mais pour la fonction elle-même c'est différent, on peut donc choisir ce que l'on veut et donc ici la meilleure optimisation.Le résultat est la copie de l'expression dans le return, le type de retour non référence correspond toujours à une copie (parfois éludée par optimisation.)
    Je comprends maintenant beaucoup mieux d'où provient la copie supplémentaire qui me tracassait, du moins je crois le comprendre;-) À confirmer sur d'autres essais...

    Citation Envoyé par dalfab Voir le message
    Justement quand on fait A+B on veut obtenir un objet résultat mais A et B ne doivent pas être modifiés. L'instance courante ne doit surtout pas être modifiée.
    Ok, c'est aussi clair.

    Citation Envoyé par dalfab Voir le message
    On ne doit normalement jamais définir l'opérateur et le constructeur par copie, le compilateur sait le faire à ta place. Il ne faut le faire que pour gérer des problèmes particuliers de ressources ou d'allocation mémoire. Voir la règle des 5.
    Ici je l'ai juste fait pour voir plus en détail ce qui se passe dans mon dos. Autrement, oui, il n'y a pas de raisons d'y toucher avec mon exemple.

    Citation Envoyé par dalfab Voir le message
    En effet, une somme est toujours une rvalue, mais si on l'affecte immédiatement à une lvalue, il y aura optimisation NRVO.
    Y a-t-il moyen de savoir quand il y aura une optimisation NVRO ou non ? Cela est-il trop dépendant du système et/ou du compilateur ?


    Citation Envoyé par dalfab Voir le message
    Pourquoi const? on fait ce qu'on veut du résultat pourquoi empêcher les modifications?
    si on écrit A + B + C, c'est calculé operator+( operator+(A,B) , C ), si operator+(A,B) retourne un objet constant, quand on ajoute C, on doit faire operator+( rvalue_constante , C ), et si le premier paramètre attendu est une copie, il y aura copie pour ne pas modifier la constante. Si la rvalue n'est pas constante elle est directement réutilisée comme étant la copie.
    Je vois le principe, mais que se passerait-il alors si on écrivait quelque chose du genre : Cela n'a pas de sens de mettre une somme en lvalue et le const en retour permettrait d'éviter cela, non ?


    Ce qui fait que je me pose aussi des questions concernant le retour par référence !!
    J'ai fait beaucoup de C (pas de soucis avec les pointeurs), mais là je bloque sur le retour par référence, car la variable en lvalue ne peut pas être du type référence : une référence n'est pas un pointeur et on ne peut pas modifier une référence par affectation. Quand est-ce que le retour par référence est-il alors utile ?

    Ou alors, un retour par référence ne serait utile que si ce retour doit être utilisé directement comme rvalue à la suite et éviter alors une copie intermédiaire ?

    De plus, on ne peut pas renvoyer une référence sur une variable locale à une fonction qui contiendrait le résultat du retour sous peine de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dev_surcharge.cpp:182:9: warning: reference to stack memory associated with
          local variable 't' returned [-Wreturn-stack-address]
            return t;
    avec le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Test &return_ref( Test &);
    Test &return_ref( Test &t){
    	Test tmp = {"return_ref", 7};
     
    	t += tmp;
    	return t;
    }

    Donc la référence du retour doit être une référence sur une variable dans la portée de la fonction ? Ce qui correspond bien aux cas des auto-affectations +=, -=,...
    Mais à part dans ces cas ??

  7. #7
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    809
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 809
    Points : 4 209
    Points
    4 209

    Par défaut

    Citation Envoyé par mokaude Voir le message
    Y a-t-il moyen de savoir quand il y aura une optimisation NVRO ou non ? Cela est-il trop dépendant du système et/ou du compilateur ?
    Cela dépend de beaucoup de chose, les compilateurs n'avaient pas vraiment d'obligations avant le C++17. Depuis le cadre est plus précis, je ne le connais pas assez bien pour l'expliquer ici. L'idée est : la fonction est en train de construire un objet, puis au moment du return devra le copier dans une variable qui recevra le résultat, on pourrait directement faire que l'objet construit soit directement l'objet externe qui reçoit le return.
    Citation Envoyé par mokaude Voir le message
    Je vois le principe, mais que se passerait-il alors si on écrivait quelque chose du genre : Cela n'a pas de sens de mettre une somme en lvalue et le const en retour permettrait d'éviter cela, non ??
    A+B est une rvalue donc ne peut pas être à gauche d'un égal qu'elle soit constante ou pas.
    Citation Envoyé par mokaude Voir le message
    Ce qui fait que je me pose aussi des questions concernant le retour par référence !!
    J'ai fait beaucoup de C (pas de soucis avec les pointeurs), mais là je bloque sur le retour par référence, car la variable en lvalue ne peut pas être du type référence : une référence n'est pas un pointeur et on ne peut pas modifier une référence par affectation. Quand est-ce que le retour par référence est-il alors utile ?
    Si une fonction retourne une référence, elle doit fournir une lvalue qui doit exister au delà de la fonction. C'est le même problème que retourner un pointeur, il ne doit pas pointer sur une locale.
    Citation Envoyé par mokaude Voir le message
    Ou alors, un retour par référence ne serait utile que si ce retour doit être utilisé directement comme rvalue à la suite et éviter alors une copie intermédiaire ??
    Pas vraiment, il faut considérer tous les cas possibles de retour. Quand une fonction ou un opérateur retourne une l-référence, on peut :
    * conserver cette référence (mais attention, elle peut correspondre à un objet qui va disparaître, on aura alors une 'dangling' référence.)
    * en faire immédiatement un copie.
    * ça peut être un objet intermédiaire dans une expression. Dans ce cas la l-référence est transmise à la fonction suivante qui peut indifféremment directement l'utiliser comme une l-référence ou en faire une copie.
    Citation Envoyé par mokaude Voir le message
    De plus, on ne peut pas renvoyer une référence sur une variable locale à une fonction qui contiendrait le résultat du retour sous peine de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dev_surcharge.cpp:182:9: warning: reference to stack memory associated with
          local variable 't' returned [-Wreturn-stack-address]
            return t;
    avec le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Test &return_ref( Test &);
    Test &return_ref( Test &t){
    	Test tmp = {"return_ref", 7};
     
    	t += tmp;
    	return t;
    }
    Donc la référence du retour doit être une référence sur une variable dans la portée de la fonction ? Ce qui correspond bien aux cas des auto-affectations +=, -=,...
    Mais à part dans ces cas ??
    Voir ma réponse précédente. Ce qui est référencé doit correspondre à un objet qui existera encore à la sortie de la fonction, mais attention cela peut être une r-value comme ci-dessous!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct Test {
       int  x = 2;
    };
    int &ret_ref( int &t ) {
        return t;
    }
    int main() {
       Test   t;
       ret_ref( 3 );                     // erreur de compilation 3 est une rvalue
       int   y = ret_ref( t.x );         // Ok, y a reçu une copie de la référence reçue donc copie de t.x
       int   v = ret_ref( ret_ref( ret_ref( t.x ) ) );  // Ok, on conserve une copie
       int  &w = ret_ref( t.x );         // Ok, mais attention w peut être utilisé tant que t existe puis il y aura "Undefined Behavior"
       int   z = ret_ref( Test{}.x );    // Ok, Test{} est une rvalue, z reçoit une copie de son champ x
       int  &u = ret_ref( Test{}.x );    // "Undefined Behavior", u référence un champ x qui n'existe plus à la fin de l'expression.
    }

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    août 2015
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : août 2015
    Messages : 6
    Points : 5
    Points
    5

    Par défaut

    Merci beaucoup pour toutes ces informations (surtout le dernier exemple très pertinent ;-) et aussi pour le temps passé à répondre à mes interrogations.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 9
    Dernier message: 02/09/2007, 22h39
  2. Erreur lors de la création d'une variable
    Par neuro6 dans le forum C++
    Réponses: 5
    Dernier message: 22/01/2007, 09h40
  3. Création d'une Variable sous BO
    Par HBA_BO dans le forum Deski
    Réponses: 3
    Dernier message: 25/10/2006, 15h46
  4. Création d'une variable de session avec un ID
    Par PrinceMaster77 dans le forum ASP
    Réponses: 4
    Dernier message: 18/10/2004, 11h28
  5. [BES] Création d'une variable d'environnement
    Par NGI80 dans le forum Autres
    Réponses: 2
    Dernier message: 17/10/2002, 08h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo