erreur: initialisation non valide d'une référence non-const de type 'int &' à partir d'une rvalue de type 'int'

87

Mauvaise forme:

int &z = 12;

Forme correcte:

int y;
int &r = y;

Question :
Pourquoi le premier code est-il erroné? Quelle est la « signification » de l'erreur dans le titre?

Aquarius_Girl
la source
4
Les temporaires ne peuvent pas être liés à des références non constantes. int (12) est un temporaire dans ce cas.
Prasoon Saurav
@PrasoonSaurav Qu'entendez-vous par temporaire 12? Manque de concepts ici (de ma part :))
Aquarius_Girl
2
Notez qu'il n'y a pas de raison technique stricte pour cette restriction. Il aurait été tout aussi facile à mettre en œuvre de permettre des références mutables aux temporaires. L'interdire est une décision de conception de C ++, car une telle construction serait une mauvaise conception avec un risque beaucoup plus grand d'être abusé par inadvertance qu'un véritable utilitaire. (Je n'ai trouvé qu'une fois le besoin artificiel d'une telle chose.)
Kerrek SB
@KerrekSB le besoin le plus courant d'une référence de liaison à un objet rvalue est probablement(ostringstream() << "x=" << x).str()
curiousguy
@curiousguy: Oui, c'est le contenu du lien que j'ai publié.
Kerrek SB

Réponses:

132

C ++ 03 3.10 / 1 dit: "Chaque expression est une lvalue ou une rvalue." Il est important de se rappeler que la valeur par rapport à la valeur est une propriété des expressions et non des objets.

Lvalues ​​nomme les objets qui persistent au-delà d'une seule expression. Par exemple, obj, *ptr, ptr[index]et ++xsont tous lvalues.

Les valeurs R sont des valeurs temporaires qui s'évaporent à la fin de l'expression complète dans laquelle elles vivent ("au point-virgule"). Par exemple, 1729, x + y, std::string("meow")et x++sont tous rvalues.

L'opérateur d'adresse de demande que son "opérande doit être une valeur l". si nous pouvons prendre l'adresse d'une expression, l'expression est une lvalue, sinon c'est une rvalue.

 &obj; //  valid
 &12;  //invalid
BruceAdi
la source
2
" si nous pouvions prendre l'adresse d'une expression, l'expression est une lvalue, sinon c'est une rvalue. " si seulement C ++ était aussi simple! (mais la nuance n'est pas vraiment pertinente ici) "Les valeurs R sont temporaires" quoi? objets?
curiousguy
2
Les @curiousguyRvalues ​​sont des temporaires qui disparaissent à la fin de la pleine expression dans laquelle elles vivent. Pour simplement répondre pourquoi l'express int & = 12;est invalide, le standard dit qu'un littéral de chaîne est une lvalue, les autres littéraux sont des rvalues.
BruceAdi
@curiousguy: Oui. Les valeurs R sont des objets temporaires , mais toutes les valeurs r ne sont pas des objets temporaires; certains ne sont même pas des objets.
Nawaz
" Par exemple, 1729, x + y, std :: string (" meow ") et x ++ sont tous des rvalues. " Mais std::string("meow")construit un objet de type std::stringet renvoie une rvalue qui désigne cet objet, 1729n'a pas d'effet secondaire et donne une valeur 1729 comme rvaleur de type int.
curiousguy
1
@curiousguy: La déclaration "Lvalues name objects that persist beyond a single expression."est une déclaration 100% correcte. Au contraire, votre exemple (const int &)1est incorrect, car ce n'est PAS un objet "nommé".
Nawaz
53
int &z = 12;

Sur le côté droit, un objet temporaire de type intest créé à partir du littéral intégral 12, mais le temporaire ne peut pas être lié à une référence non const. D'où l'erreur. C'est la même chose que:

int &z = int(12); //still same error

Pourquoi un temporaire est créé? Parce qu'une référence doit faire référence à un objet dans la mémoire, et pour qu'un objet existe, il doit d'abord être créé. Puisque l'objet n'est pas nommé, il s'agit d'un objet temporaire . Il n'a pas de nom. À partir de cette explication, il est devenu assez clair pourquoi le deuxième cas est bon.

Un objet temporaire peut être lié à une référence const, ce qui signifie que vous pouvez le faire:

const int &z = 12; //ok

Référence C ++ 11 et Rvalue:

Par souci d'exhaustivité, je voudrais ajouter que C ++ 11 a introduit rvalue-reference, qui peut se lier à un objet temporaire. Donc, en C ++ 11, vous pouvez écrire ceci:

int && z = 12; //C+11 only 

Notez qu'il y a au &&lieu de &. Notez également que ce constn'est plus nécessaire, même si l'objet auquel zse lie est un objet temporaire créé à partir d'intégrale-littérale 12.

Depuis que C ++ 11 a introduit rvalue-reference , il int&est désormais appelé lvalue-reference .

Nawaz
la source
10

12est une constante de compilation qui ne peut pas être modifiée contrairement aux données référencées par int&. Ce que tu peux faire c'est

const int& z = 12;
Michael Krelin - hacker
la source
2
@curiousguy, soyez cohérent, vous avez dit "Il faut comprendre que ce sont des règles C ++. Elles existent, et n'ont pas besoin de justification" (sans parler de la première édition). Comment dois-je interpréter votre plainte de ne pas donner ce qui est selon vous n'est pas nécessaire?
Michael Krelin - hacker
2
@ MichaelKrelin-hacker: Techniquement non, vous ne pouvez (jamais) lier une référence à une valeur (ou à une constante de temps de compilation), la norme est assez explicite quant à ce qui se passe réellement: sinon, un temporaire de type «cv1 T1» est créé et initialisé à partir de l'expression d'initialisation en utilisant les règles pour une initialisation de copie sans référence (8.5). La référence est alors liée au temporaire C'est-à-dire que la syntaxe est autorisée, mais la sémantique ne consiste pas à lier une référence à la constante, mais plutôt à la lier au temporaire qui est implicitement créé.
David Rodríguez - dribeas
1
@curiousguy: Les règles de la langue font partie de la conception, et le plus souvent, il y a des justifications pour expliquer pourquoi la langue a été conçue comme telle. Dans ce cas particulier, comme en C, vous n'êtes pas autorisé à prendre l'adresse d'une valeur (elle n'en a pas), ni à lier une référence. Considérons maintenant une fonction void f( vector<int> const & ), qui est idiomatique pour passer un vecteur qui ne doit pas être modifié. Le problème maintenant est que ce f( vector<int>(5) )serait incorrect et que l'utilisateur devrait fournir une surcharge différente, void f( vector<int> v ) { f(v); }ce qui est trivial.
David Rodríguez - dribeas
1
... maintenant parce que cela serait pénible pour les utilisateurs du langage, les concepteurs ont décidé que le compilateur effectuerait l'opération équivalente pour vous, dans l'appel f( vector<int>(5) ), le compilateur crée un temporaire, puis lie la référence à ce temporaire, et de même s'il y avait une conversion implicite de 5directement. Cela permet au compilateur de générer une signature unique pour la fonction et permet une implémentation de la fonction par un seul utilisateur. À partir de là, un comportement similaire est défini pour le reste des utilisations de références constantes pour la cohérence.
David Rodríguez - dribeas
1
@ MichaelKrelin-hacker: les références sont des alias d'objets et une valeur n'est pas un objet. Selon le contexte, les références peuvent être simplement des alias, le compilateur supprime la référence et utilise simplement l'identifiant pour signifier ce que signifie l'objet d'origine ( T const & r = *ptr;, toute utilisation ultérieure de rdans la fonction peut être remplacée par *ptr, et rn'a pas besoin d'exister au moment de l'exécution) ou il peut devoir être implémenté en conservant l'adresse de l'objet qu'il alias (envisagez de stocker une référence en tant que membre d'un objet) - qui est implémenté comme un pointeur auto-référencé.
David Rodríguez - dribeas
4

La liaison de référence non-const et const suit des règles différentes

Voici les règles du langage C ++:

  • une expression constituée d'un nombre littéral ( 12) est une "rvalue"
  • il n'est pas permis de créer une référence non-const avec une rvalue: int &ri = 12;est mal formée
  • il est permis de créer une référence const avec une rvalue: dans ce cas, un objet sans nom est créé par le compilateur; cet objet persistera tant que la référence elle-même existera.

Vous devez comprendre que ce sont des règles C ++. Ils sont juste.

Il est facile d'inventer un langage différent, disons C ++, avec des règles légèrement différentes. En C ++ ', il serait permis de créer une référence non-const avec une rvalue. Il n'y a rien d'incohérent ni d'impossible ici.

Mais cela permettrait un code risqué où le programmeur pourrait ne pas obtenir ce qu'il voulait, et les concepteurs C ++ ont à juste titre décidé d'éviter ce risque.

curieux
la source
0

Les références sont des "pointeurs cachés" (non nuls) vers des choses qui peuvent changer (lvalues). Vous ne pouvez pas les définir comme une constante. Ce devrait être une chose "variable".

ÉDITER::

Je pense à

int &x = y;

comme presque équivalent de

int* __px = &y;
#define x (*__px)

__pxest un nouveau nom, et les #define xœuvres uniquement à l'intérieur du bloc contenant la déclaration de xréférence.

Basile Starynkevitch
la source
1
Pourquoi vous pouvez, si la référence est const:)
Michael Krelin - hacker
Mais l'exemple de l'affiche n'était pasconst
Basile Starynkevitch
Oui, je voulais dire votre libellé «les références sont des pointeurs vers des choses qui peuvent changer» - il s'agit de références sans coût.
Michael Krelin - hacker
"Les références sont des" pointeurs cachés " " faux " vers des choses qui peuvent changer de " mauvaises " choses qui peuvent changer (lvalues). " Faux
curiousguy
@Loki: pourrais-tu expliquer plus?
Basile Starynkevitch