Comment une non-const de référence ne peut pas se lier à un objet temporaire?
Pourquoi n'est-il pas permis d'obtenir des non-const référence à un objet temporaire,
la fonction getx()
retourne? Clairement, c'est interdit par la Norme C++
mais je suis intéressé dans le but d'une telle restriction, pas une référence à la norme.
struct X
{
X& ref() { return *this; }
};
X getx() { return X();}
void g(X & x) {}
int f()
{
const X& x = getx(); //OK
X& x = getx(); //error
X& x = getx().ref(); //OK
g(getx()); //error
g(getx().ref()); //OK
return 0;
}
- Il est clair que la durée de vie de l'objet ne peut pas être la cause, parce que
la constante référence à un objet est qui ne sont pas interdites par la Norme C++. - Il est clair que l'objet temporaire n'est pas constante dans l'exemple ci-dessus, parce que les appels à la non-fonctions constantes sont autorisés. Par exemple,
ref()
pourrait modifier l'objet temporaire. - En outre,
ref()
permet de tromper le compilateur et obtenir un lien vers cet objet temporaire et qui permet de résoudre notre problème.
En outre:
Ils disent "l'affectation d'un objet temporaire pour la const référence prolonge la durée de vie de cet objet" et "Rien n'est dit au sujet de la non-const références bien".
Mon question supplémentaire. Ne affectation suivante prolonger la durée de vie de l'objet temporaire?
X& x = getx().ref(); //OK
Je suis en désaccord avec "la durée de vie de l'objet ne peut pas être la cause de la partie", juste parce qu'elle est mentionnée dans la norme, que l'attribution d'un objet temporaire pour la const de référence s'étend la durée de vie de cet objet à la durée de vie de la const de référence. Rien n'est dit au sujet de la non-const références...
Eh bien, quelle est la cause de ce "Rien n'est dit au sujet de la non-const références si...". C'est une partie de ma question. Est-il un sens à tout cela? Peut-être les auteurs de la Norme juste oublié non-const références et bientôt, nous allons voir la prochaine Question centrale?
GotW #88: Un Candidat Pour le "const". herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entrée
VC lie rvalues à la non-const références. Ils appellent cela une fonction, mais c'est vraiment un bug. (Notez que ce n'est pas un bug parce que c'est fondamentalement illogique, mais parce qu'il a été exclu explicitement à prévenir les erreurs stupides.)
Herb Sutter est GotW #88 : Candidat à la "Plus Importante const" a été déplacé.
Eh bien, quelle est la cause de ce "Rien n'est dit au sujet de la non-const références si...". C'est une partie de ma question. Est-il un sens à tout cela? Peut-être les auteurs de la Norme juste oublié non-const références et bientôt, nous allons voir la prochaine Question centrale?
GotW #88: Un Candidat Pour le "const". herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entrée
VC lie rvalues à la non-const références. Ils appellent cela une fonction, mais c'est vraiment un bug. (Notez que ce n'est pas un bug parce que c'est fondamentalement illogique, mais parce qu'il a été exclu explicitement à prévenir les erreurs stupides.)
Herb Sutter est GotW #88 : Candidat à la "Plus Importante const" a été déplacé.
OriginalL'auteur Alexey Malistov | 2009-10-14
Vous devez vous connecter pour publier un commentaire.
De cette Visual C++ article de blog sur les références rvalue:
Fondamentalement, vous ne devriez pas essayer de modifier temporaires pour la raison qu'ils sont des objets temporaires et va mourir à tout moment maintenant. La raison pour laquelle vous êtes autorisé à appeler non-const méthodes est que, eh bien, vous êtes les bienvenus pour faire un peu de "stupide" choses aussi longtemps que vous savez ce que vous faites et vous sont explicites à ce sujet (comme, à l'aide de reinterpret_cast). Mais si vous liez un temporaire à un non-const de référence, vous pouvez garder le passant autour de "pour toujours" juste pour avoir votre manipulation de l'objet disparaître, parce que quelque part le long du chemin, vous avez complètement oublié que c'était temporaire.
Si j'étais vous, je voudrais repenser la conception de mes fonctions. Pourquoi est g() acceptant de référence, est-il de modifier le paramètre? Si non, faire const référence, si oui, pourquoi avez-vous essayez de passe temporaire à elle, vous n'avez pas de soins temporaire de modification? Pourquoi est-getx() renvoi temporaire de toute façon? Si vous partagez avec nous votre vrai scénario et de ce que vous voulez accomplir, vous pouvez obtenir quelques bonnes suggestions sur la façon de le faire.
Aller à l'encontre de la langue et de tromper le compilateur rarement résout des problèmes - habituellement, il crée des problèmes.
Edit: répondre à des questions en commentaire:
1)
X& x = getx().ref(); //OK when will x die?
- je ne sais pas et je n'aime pas, parce que c'est exactement ce que je veux dire par "aller à l'encontre de la langue". Le langage dit "temporaires mourir à la fin de l'instruction, sauf s'ils sont liés à const référence, dans ce cas, ils meurent lorsque la référence est hors de portée". L'application de cette règle, il semble que x est déjà mort au début de la prochaine instruction, car il n'est pas lié à const de référence (le compilateur ne sait pas ce qu'ref() retourne). C'est juste une supposition.2) je l'ai dit le but clairement: vous n'êtes pas autorisé à modifier temporaires, parce qu'il n'a tout simplement pas de sens (en ignorant C++0x références rvalue). La question "alors, pourquoi suis-je autorisé à appeler non-const membres?" est une bonne idée, mais je n'ai pas de meilleure réponse que celle que j'ai déjà indiqué ci-dessus.
3) Ainsi, si je suis à droite sur x dans
X& x = getx().ref();
mourir à la fin de l'énoncé, le problème est évident.De toute façon, basé sur votre question et les commentaires que je ne pense pas que même ces réponses seront vous satisfaire. Voici une tentative finale/résumé: Le C++ comité a décidé qu'il n'est pas judicieux de modifier temporaires, par conséquent, ils ont refusé de liaison à la non-const références. Peut-être certains d'implémentation de compilateur ou de l'historique des questions ont également été impliqués, je ne sais pas. Puis, quelques cas spécifiques ont émergé, et il a été décidé que, contre toute attente, ils auront toujours la possibilité de la modification directe par le biais de l'appel à la non-const méthode. Mais c'est une exception - vous n'êtes pas autorisés à modifier temporaires. Oui, le C++ est souvent bizarre.
2) en Fait, vous est autorisé à modifier rvalues (temporaires). Il est interdit pour les types intégrés (
int
etc.), mais il est permis pour les types définis par l'utilisateur:(std::string("A")+"B").append("C")
.3) La raison Stroustrup donne (dans D&E) pour l'interdiction de la liaison de rvalues à la non-const références, c'est que, si Alexey du
g()
serait de modifier l'objet (ce que vous attendez d'une fonction prenant un non-const de référence), il serait de modifier un objet qui va mourir, de sorte que personne ne pouvait m'atteindre la valeur de modification de toute façon. Il dit que ce, le plus probable, est une erreur.Je suis désolé si je t'ai offensé, mais je ne pense pas que 2) est tatillon. Rvalues ne sont tout simplement pas const, à moins que vous leur faites donc, et vous pouvez les modifier, sauf s'ils sont encastrés. Il m'a fallu un certain temps pour comprendre que, avec, par exemple, l'exemple de chaîne, je fais quelque chose de mal (JFTR: je n'ai pas), j'ai donc tendance à prendre cette distinction grave.
Je suis d'accord avec la sbi - cette question est pas du tout tatillon. C'est sur la base de la sémantique de déplacement, ce type de classe rvalues sont le mieux gardé de non-const.
OriginalL'auteur sbk
Dans votre code
getx()
retourne un objet temporaire, un soi-disant "rvalue". Vous pouvez copier des rvalues dans les objets (aka. les variables) ou les lier à d'const références (ce qui permettra de prolonger leur durée de vie jusqu'à la fin de la référence de la vie). Vous ne pouvez pas lier rvalues à la non-const références.C'était délibérée d'une décision de conception afin d'empêcher les utilisateurs de modifier accidentellement un objet qui va mourir à la fin de l'expression:
Si vous voulez faire cela, vous devrez soit faire une copie locale ou de l'objet ou de le lier à un const référence:
Noter que le prochain C++ standard comprendra références rvalue. Ce que vous connaissez des références est donc plus à être appelé "lvalue références". Vous serez autorisé à lier rvalues de références rvalue et vous pouvez la surcharge de fonctions sur "rvalue-ness":
L'idée derrière références rvalue est que, depuis que ces objets vont mourir de toute façon, vous pouvez tirer parti de cette connaissance et de mettre en œuvre ce qu'on appelle "la sémantique de déplacement", un certain type d'optimisation:
g(getx())
ne fonctionne pas parce que sa signature estg(X& x)
etget(x)
retourne un objet temporaire, donc on ne peut pas lier un objet temporaire (rvalue) à un non-référence constante, correct? Et dans votre premier morceau de code, je pense que ça va êtreconst X& x2 = getx();
au lieu deconst X& x1 = getx();
..Merci d'avoir signalé ce bug dans ma réponse 5 ans après je l'ai écrit!
:-/
Oui, votre raisonnement est correct, quoique un peu à l'envers: On ne peut pas lier temporaires à des non-const
(lvalue) références, et donc temporaire retourné pargetx()
(et pasget(x)
) ne peut pas être liée à la lvalue de référence qui est l'argument deg()
.Umm, qu'as-tu dire par
getx()
(et pasget(x)
)?Quand j'écris "...getx() (et non pas des(x))...", je veux dire que le nom de la fonction est
getx()
, et pasget(x)
(comme vous l'avez écrit).Oh ouais, ouais, bien sûr. Merci.. 🙂
OriginalL'auteur sbi
Ce que vous montrer, c'est que l'opérateur de chaînage est autorisé.
L'expression est " getx().ref (); "et cela est exécuté à l'accomplissement avant l'affectation à "x".
Noter que getx() ne retourne pas une référence mais entièrement formé de l'objet dans le contexte local. L'objet est temporaire, mais elle est pas const, vous permettant ainsi de faire appel à d'autres méthodes pour calculer une valeur ou avoir d'autres effets secondaires se produire.
Regardez à la fin de cette réponse pour un meilleur exemple de cette
Vous pouvez pas lier temporaire à une référence, car cela va générer une référence à un objet qui sera détruit à la fin de l'expression, vous laissant avec un bancales de référence (qui est en désordre, et le standard n'aime pas le désordre).
La valeur retournée par ref() est une référence valide, mais la méthode ne fait pas attention à la durée de vie de l'objet, il est de retour (car il ne peut pas disposer de cette information dans son contexte). Vous avez juste fait l'équivalent de:
La raison pour laquelle il est OK pour le faire avec un const référence à un objet temporaire est que la norme prolonge la durée de vie de la temporaire de la durée de vie de la référence pour les objets temporaires durée de vie est prolongée au-delà de la fin de l'instruction.
Donc, la seule question qui reste est pourquoi la norme souhaitez pas autoriser référence aux temporaires pour prolonger la durée de vie de l'objet au-delà de la fin de l'instruction?
Je crois que c'est parce que dans ce cas, le compilateur très dur pour obtenir de bons pour des objets temporaires. Il a été fait pour const références temporaires comme cela a limité l'utilisation et donc vous a forcé à faire une copie de l'objet pour en faire quelque chose d'utile, mais ne fournissent certaines des fonctionnalités limitées.
Pense de cette situation:
Prolonger la durée de vie de cet objet temporaire va être très déroutant.
Tandis que le suivant:
Va vous donner le code qu'il est intuitif à utiliser et à comprendre.
Si vous souhaitez modifier la valeur que vous devriez être en retournant une valeur à une variable. Si vous êtes en essayant d'éviter le coût de la copie de la obejct de retour de la fonction (comme il semble que l'objet est exemplaire construit à l'arrière (techniquement)). Alors ne vous embêtez pas à le compilateur est très bon à 'La Valeur De Retour D'Optimisation"
En balançant les références ne sont pas seulement en désordre. Ils pourraient conduire à de sérieux bugs lors de l'accès plus tard dans la méthode!
Notez que le fait que la liaison à un const référence améliore temporairement la durée de vie est exception qui a été ajouté délibérément (TTBOMK afin de permettre à de manuel optimisations). Il n'y avait pas une exception ajoutée pour les non-const références, car la liaison temporaire à un non-const de référence a été vu pour la plus susceptible d'être un programmeur d'erreur.
C'était de mon point de vue 😉
Une référence à un invisable variable! Non pas que intuative.
OriginalL'auteur Martin York
Pourquoi est discuté dans le C++ FAQ (boldfacing le mien):
x * 0
donnex
? Quoi? Quoi??Ce dernier argument est particulièrement faible. Pas de compilateur vaut la peine de mentionner serait jamais réellement changer la valeur de 0 à 1, ou même d'interpréter
incr(0);
dans une telle voie. Évidemment, si c'était autorisé, il serait interprété comme créant une temporaire entier et en passant àincr()
OriginalL'auteur Tony Delroy
Le principal problème est que
est une erreur de logique:
g
, c'est de modifier le résultat degetx()
mais vous n'avez aucune chance d'examiner l'objet modifié. Sig
n'a pas besoin de modifier son paramètre, alors il n'aurait pas tenu une lvalue de référence, il aurait pu prendre le paramètre par valeur ou par référence const.est valide parce que vous avez parfois besoin de réutiliser le résultat d'une expression, et il est assez clair que vous avez affaire à un objet temporaire.
Cependant, il n'est pas possible de faire
valide sans faire
g(getx())
valide, ce qui est ce que la langue concepteurs ont essayé d'éviter en premier lieu.est valide parce que les méthodes ne connais que la const-ness de la
this
, ils ne savent pas si ils sont appelés sur une lvalue ou sur une rvalue.Comme toujours en C++, vous avez une solution pour contourner cette règle, mais vous devez signaler au compilateur que vous savez ce que vous faites en étant explicite:
OriginalL'auteur Dan Berindei
Semble que la question d'origine de pourquoi ce n'est pas permis a été répondu clairement: "car il est le plus probablement une erreur".
FWIW, je pensais que je voudrais montrer comment pourrait être fait, même si je ne pense pas que c'est une bonne technique.
La raison que j'ai parfois envie de passer temporairement à une méthode de prendre une non-const de référence est intentionnellement jeter une valeur retournée par référence à la méthode d'appel ne se soucie pas. Quelque chose comme ceci:
Comme expliqué dans les réponses précédentes, qui ne compile pas. Mais cette compile et fonctionne correctement (avec mon compilateur):
Cela montre simplement que vous pouvez utiliser la conversion de mentir pour le compilateur. Bien évidemment, il serait beaucoup plus propre à déclarer et à passer un inutilisés variable automatique:
Cette technique introduit une inutiles variable locale à la méthode du champ d'application. Si pour une raison quelconque vous voulez l'empêcher d'être utilisé plus tard dans la méthode, par exemple, pour éviter toute confusion ou d'erreur, vous pouvez cacher dans un bloc local:
-- Chris
OriginalL'auteur Chris Pearson
Pourquoi voudriez-vous jamais eu envie
X& x = getx();
? Utilisez simplementX x = getx();
et de s'appuyer sur RVO.g(getx())
plutôt queg(getx().ref())
ce n'est pas une vraie raison. Si vous le faites, alors vous avez une erreur de logique soemwhere, parce que
g
va modifier quelque chose qui vous ne pouvez pas obtenir vos mains sur les plus.peut-être qu'il ne s'inquiète pas.
"s'appuyer sur RVO", sauf qu'il n'est pas appelé "RVO".
C'est un très accepté terme. Il n'y a absolument rien de mal avec le décrivant comme "un RVO".
OriginalL'auteur DevFred
Le mal de solution de contournement consiste à le "mutable" mot. Effectivement le mal est laissé comme exercice pour le lecteur. Ou voir ici: http://www.ddj.com/cpp/184403758
OriginalL'auteur paul
Excellente question, et voici ma tentative de plus concise réponse (puisque beaucoup d'informations utiles est dans les commentaires et difficile à creuser dans le bruit).
Toute référence lié directement temporaire permettra de prolonger sa durée de vie [12.2.5]. D'autre part, une référence initialisé avec une autre référence pas (même si au final c'est la même temporaire). Qui fait sens (le compilateur ne sait pas ce que la référence en fin de compte se réfère).
Mais cette idée est extrêmement déroutant. E. g.
const X &x = X();
fera temporaire durer aussi longtemps que lax
de référence, maisconst X &x = X().ref();
ne sera PAS (qui sait ce queref()
effectivement retournés). Dans ce dernier cas, le destructeur deX
est appelée à la fin de cette ligne. (Ce qui est observable avec un non-trivial destructeur.)Il semble donc généralement confus et dangereux (pourquoi compliquer les règles concernant l'objet de la durée de vie?), mais sans doute il était nécessaire, au moins pour const références, de sorte que la norme ne régler ce problème pour eux.
Tous les temporaires ne persistent jusqu'à la fin de l'expression. De faire usage d'entre eux, cependant, vous avez besoin d'un truc comme vous avez avec
ref()
. C'est légal. Il ne semble pas être une bonne raison de l'obstacle supplémentaire à franchir, sauf pour rappeler que le programmeur que quelque chose d'inhabituel se passe (à savoir, un paramètre de référence dont les modifications seront rapidement perdu).OriginalL'auteur DS.
"Il est clair que l'objet temporaire n'est pas constante dans l'exemple ci-dessus, parce que les appels
pour les non-fonctions constantes sont autorisés. Par exemple, ref() pourrait modifier le temporaire
l'objet".
Dans votre exemple getX() ne renvoie pas un const X de sorte que vous êtes en mesure d'appeler ref() dans beaucoup de la même manière que l'on pourrait appeler X().ref(). Vous êtes de retour d'une non const ref et peut donc appel non const méthodes, ce que vous ne pouvez pas faire est d'attribuer la ref à un non const référence.
Avec SadSidos commentaire, ce qui rend votre trois points incorrecte.
OriginalL'auteur Patrick
J'ai un scénario que je voudrais partager où je souhaite que je pourrais faire ce que Alexey demande. Dans un Maya C++ plugin, je dois faire la suite shenanigan afin d'obtenir une valeur dans un attribut node:
C'est fastidieux à écrire, ce que j'ai écrit la suite des fonctions d'assistance:
Et maintenant, je peux écrire le code à partir du début de la poste en tant que:
Le problème est qu'il ne compile pas en dehors de Visual C++, parce que c'est en essayant de lier la variable temporaire retourné à partir de l'opérateur | le MPlug de référence de la << opérateur. Je voudrais qu'il soit une référence parce que ce code est appelé plusieurs fois et je préfère ne pas avoir MPlug être copié autant. J'ai seulement besoin de l'objet temporaire de vivre jusqu'à la fin de la deuxième fonction.
Bien, c'est mon scénario. Juste pensé que je voudrais vous montrer un exemple de cas où l'on voudrait faire ce Alexey décrire. Je souhaite la bienvenue à toutes les critiques et suggestions!
Grâce.
OriginalL'auteur Robert Trussardi