Comment une déclaration comme (a = b) = c;
travailler en C++, en supposant a
, b
y c
sont int
ou tout autre type primitif ?
Serait-ce (a ? b : c) = d;
signifie alors qu'en fonction de la valeur de a
, d
sera attribué à b
o c
?
Comment une déclaration comme (a = b) = c;
travailler en C++, en supposant a
, b
y c
sont int
ou tout autre type primitif ?
L'expression d'affectation a = b
n'est pas une lvalue en C, mais elle l'est en C++ :
C11, 6.5.14 (Opérateurs d'assignation) :
Un opérateur d'affectation stocke une valeur dans l'objet désigné par l'opérande de gauche. Une expression d'affectation a la valeur de l'opérande de gauche après l'affectation, mais n'est pas une lvalue .
C++14, 5.18 [expr.ass] (opérateurs d'assignation et d'assignation composée) :
L'opérateur d'affectation (
=
) et les opérateurs d'affectation composés regroupent tous de droite à gauche. Tous requièrent une lvalue modifiable comme opérande gauche. et retourner une valeur l se référant à l'opérande de gauche.
Dans l'évolution du C++ à partir du C, plusieurs expressions ont été rendues "lvalue-aware", pour ainsi dire, parce que les lvalues sont beaucoup plus importantes en C++ qu'en C. En C, tout est trivial (trivialement copiable et trivialement destructible, tout cela dans les mots du C++), donc les conversions de lvalue à rvalue (ou "conversions de lvalue", comme le C les appelle) ne sont pas douloureuses. En C++, la copie et la destruction sont des concepts non triviaux, et en faisant en sorte que les expressions préservent le caractère lvalue, on peut éviter beaucoup de copies et de destructions qui n'étaient pas nécessaires au départ.
Un autre exemple est l'expression conditionnelle ( a ? b : c
), qui n'est pas une lvalue en C, mais peut être une lvalue en C++.
Un autre artefact intéressant de cette évolution du langage est que le C a quatre durées de stockage bien définies (automatique, statique, locale au fil de l'eau, dynamique), mais en C++ cela devient plus confus, puisque les objets temporaires sont un concept non trivial en C++ qui demande presque sa propre durée de stockage. (Par exemple, Clang en a une cinquième en interne, Durée de stockage "pleine expression". .) Les temporaires sont bien sûr le résultat de la conversion de lvalue en rvalue, donc en évitant la conversion, il y a une chose de moins à se soucier.
(Veuillez noter que toute cette discussion ne s'applique qu'aux expressions du langage de base respectif. Le C++ dispose également d'une fonctionnalité distincte et sans rapport avec le langage, à savoir surcharge des opérateurs qui produit des expressions d'appel de fonction, qui ont toute la sémantique habituelle des appels de fonction et n'ont rien à voir avec les opérateurs, sauf pour la syntaxe. Par exemple, vous pouvez définir une fonction surchargée operator=
qui renvoie une valeur de pr ou void
si vous le souhaitez).
Serait-ce (a ? b : c) = d;
signifie alors qu'en fonction de la valeur de a
, d
sera attribué à b
o c
?
@PaulOgilvie : c'est correct . Vous pouvez même faire des choses comme (c ? v1 : v2).clear()
. Si l'opérateur conditionnel devait toujours aboutir à une valeur pr, vous ne pourriez jamais affecter les valeurs des opérandes.
@KerrekSB pire, (c ? v1 : v2).clear()
compilerait bien (parce que nous pouvons appeler non- const
sur les temporaires), mais ne font rien en silence. *shudders*
De manière informelle, en C++, pour les types intégrés, le résultat de la fonction a = b
est une référence à a
; vous pouvez attribuer une valeur à cette référence, comme pour toute autre référence. Ainsi, (a = b) = c
attribue la valeur de b
à a
et attribue ensuite la valeur de c
à a
.
Pour les types définis par l'utilisateur, cela peut ne pas s'appliquer, bien que l'idiome habituel soit qu'un opérateur d'affectation renvoie une référence à l'argument de gauche, de sorte que le comportement des types définis par l'utilisateur imite le comportement des types intégrés :
struct S {
S& operator=(const S& rhs) {
return *this;
}
};
Maintenant, S a, b, c; (a = b) = c;
signifie appel a.operator=(b)
qui renvoie une référence à a
; puis appeler S::operator=
sur ce résultat et c
en appelant effectivement a.operator=(c)
.
Les résultats des expressions ne sont jamais des références. Le résultat de a = b
est la valeur a
(mais l'effet secondaire de la modification a
a déjà eu lieu).
@KerrekSB - soupir. C'est pourquoi j'ai dit "officieusement". S'il vous plaît, ne tirez pas cela dans un trou à rats.
Oui, oui, je comprends... mais après avoir expliqué vingt fois et plus aux gens que les références rvalue ne sont pas rvalue, j'ai commencé à essayer d'enseigner des informations honnêtes dès le début, dans l'espoir de ne jamais produire de fausses idées en premier lieu. Ce n'est pas une critique de votre réponse, je voulais juste que ce détail soit enregistré quelque part :-)
(a = b) = c
est une instruction valide en C++. Ici, '=' fonctionne comme un opérateur d'affectation. Ici, b sera assignée à la valeur de a y c sera assignée à la valeur de a pour la préséance de droite à gauche.
Par exemple :
int a = 5;
int b = 2;
int c = 7;
int answer = (a = b) = c;
cout << answer << endl;
Sortie :
7
C'est court sur le pourquoi de la question, qui tourne autour du concept de lvalue ; également, answer
est défini, mais n'est pas utilisé.
Je viens de montrer "pourquoi" et "comment" cette affirmation est valable. L'opérateur d'assignation fonctionne pour assigner et cette déclaration est valable pour tous les opérateurs d'assignation. Et merci pour la mise à jour concernant la variable 'answer'.
Désolé, mais quelqu'un élevé au C (où la construction est illégale) n'obtiendra pas la pourquoi de votre réponse. Du moins, je ne l'ai pas fait.
Ce qui suit est une petite spéculation, alors corrigez-moi si je me trompe.
Lorsqu'ils ont inventé la surcharge d'opérateurs, ils ont dû trouver une forme générale standard d'un opérateur d'affectation pour toute classe. T
. Par exemple :
T& T::operator=(T);
T& T::operator=(const T&);
Ici, il renvoie une référence à T
au lieu de simplement T
pour faire une affectation en trois parties comme x = (y = z)
efficace, ne nécessitant pas de copie.
Il pourrait renvoyer un const
référence à T
ce qui rendrait l'affectation non souhaitée (a = b) = c
une erreur. I devinez qu'ils ne l'ont pas utilisé pour deux raisons :
const
tout le temps (les petits détails de const
-correction n'étaient pas claires à l'époque)(a = b).print()
où print
est un non const
(parce que le programmeur a été paresseux/ignorant de const
-correction)La sémantique pour les types primitifs (qui ne sont pas class
) ont été en quelque sorte extrapolés, pour donner.. :
int& operator=(int&, int); // not real code; just a concept
Le "type de retour" n'est pas const int&
afin de faire correspondre le modèle avec class
es. Donc, si le buggy (a = b) = c
est valable pour les types définis par l'utilisateur, il devrait l'être également pour les types intégrés, comme l'exige la directive sur la protection des données. Principes de conception du C++ . Et une fois que vous avez documenté ce genre de choses, vous ne pouvez pas les modifier pour des raisons de compatibilité ascendante.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
19 votes
Pourquoi est-ce illégal en Python ? Parce qu'ils sont tous différents les langages de programmation ! C n'est pas C++ n'est pas C ! Pourquoi pensez-vous que différents PLs ont la même sémantique ?
2 votes
@Olaf Ok, oublions le C, pourquoi (a = b) = c est une syntaxe C++ légale ? comment ça marche ?
9 votes
Pouvez-vous expliquer pourquoi vous pensez qu'il ne devrait pas l'être ? Cette question est un peu vide de sens.
0 votes
Prises dans le contexte de la langue, les questions de ce type ressemblent à "Pourquoi 2+2=4 ?". Il est impossible de comprendre ce que vous demandez tant que vous n'avez pas clarifié le problème spécifique que vous avez avec
(a=b)=c
. Pourquoi cela ne fonctionnerait-il pas ? (Et(a=b)=c
est juste une expression, pas encore une déclaration).0 votes
Cela change complètement la question et laisse une réponse sans contexte ! Retour en arrière.
0 votes
@Nicol Bolas : Je ne suis pas sûr que votre changement était rédactionnel. Il semble avoir supprimé la moitié de la question.
4 votes
@KerrekSB : Le commentaire du PO indique clairement que c'est la question à laquelle il voulait répondre.
1 votes
Eh bien, il n'est apparemment pas immédiatement clair pour le PO que
(a=b)
est une valeur lval valide. Je suppose que l'OP pourrait comparer cela à, disons,5=a;
ou5+b=a;
des expressions qui semblent absurdes au regard de la façon dont on pourrait comprendre le mécanisme d'un langage de programmation procédural. En termes simples,(a=b)
ressemble à une expression qui peut "sortir" une valeur mais pas "entrer" une valeur.0 votes
@nitro2k01 : Tout à fait possible ! J'aimerais entendre l'OP parler quand même :)
0 votes
@Olaf : Non, la question porte sur le C++ et nous ne mettons pas de balises dans les titres. Merci de ne pas revenir sur de telles modifications. Merci
0 votes
@LightnessRacesinOrbit : J'ai fait marche arrière parce que l'édition a laissé une réponse sans contexte. Dommage que ta réponse ait perdu son contexte, mais tu as répondu après ce montage, l'autre était avant. J'ai suivi les règles du site et vous ai explicitement notifié du retour en arrière lors de votre réponse. Voir l'historique ! Vous auriez pu modifier le titre sans modifier la question. contexte (qui était la raison du retour en arrière, pas le titre) laissant maintenant l'autre réponse sans contexte (elle cite la norme C). L'éditeur a maintenant violé les règles du site. >Vous pouvez seulement me reprocher de ne pas avoir édité le titre. après J'ai fait demi-tour !
0 votes
Note : Je n'ai pas fait de CV pour être basé sur l'opinion ; c'est un non-sens, car il y a une raison claire pour laquelle ils diffèrent comme je l'ai clairement indiqué dans mon premier commentaire. Ce n'est pas ma faute si le système regroupe tous les électeurs sous le vote majoritaire.
1 votes
@LightnessRacesinOrbit : Pour être cohérent dans la plainte : s'il vous plaît rappeler aussi le dernier éditeur ! Je m'éditerais bien moi-même, mais j'ai peur de faire quelque chose qui ne vous plairait pas. Donc vous feriez mieux de vous éditer vous-même. J'ai fini ici, ce n'est ni le temps ni l'humeur d'être blâmé pour rien !
0 votes
@Olaf : Je vous le rappelle spécifiquement parce que c'est vous qui avez fait une affirmation erronée à ce sujet dans les commentaires ! Si vous ne pouvez pas le supporter, ne le distribuez pas, etc.....
0 votes
@Olaf : "Ce n'est pas ma faute si le système regroupe tous les électeurs sous le vote majoritaire." Je déteste ça aussi. :( Je l'ai soulevé sur meta il y a des années mais ce n'est toujours pas réparé.
1 votes
@7bisso Si je comprends bien la réponse de Kerreks, il évalue b et c, et assigne le résultat de l'évaluation de c à a.
0 votes
@Lightness Races in Orbit : il n'était pas évident pour moi que l'assignation elle-même soit une lvalue valide, maintenant je comprends qu'une déclaration telle que
(a = b) = c;
est à peu près équivalent àa = b; a = c;
0 votes
Je vous demande d'intégrer ce contexte dans votre question, s'il vous plaît :) Chaque fois que vous demandez pourquoi quelque chose fonctionne, vous devez indiquer pourquoi vous pensez que cela ne devrait pas fonctionner, étant donné que cela ne fonctionne pas. hace en général, nous ne pouvons pas deviner quelle est votre idée fausse afin de la corriger. (Je ne dis pas que la question n'est pas sensée).