Pourquoi n'est-il pas autorisé à obtenir une référence non constante à un objet temporaire, quelle fonction getx()
renvoie? De toute évidence, cela est interdit par la norme C ++, mais je m'intéresse à l'objectif d'une telle restriction, et non à 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, car une référence constante à un objet est pas interdite par la norme C ++.
- Il est clair que l'objet temporaire n'est pas constant dans l'exemple ci-dessus, car les appels à des fonctions non constantes sont autorisés. Par exemple,
ref()
pourrait modifier l'objet temporaire. - En outre,
ref()
vous permet de tromper le compilateur et d'obtenir un lien vers cet objet temporaire et cela résout notre problème.
En outre:
Ils disent "assigner un objet temporaire à la référence const prolonge la durée de vie de cet objet" et "Cependant, rien n'est dit sur les références non const". Ma question supplémentaire . L'affectation suivante prolonge-t-elle la durée de vie de l'objet temporaire?
X& x = getx().ref(); // OK
Réponses:
À partir de cet article de blog Visual C ++ sur les références rvalue :
Fondamentalement, vous ne devriez pas essayer de modifier les temporaires pour la simple raison qu'ils sont des objets temporaires et mourront à tout moment. La raison pour laquelle vous êtes autorisé à appeler des méthodes non const est que, bien, vous êtes invités à faire des choses "stupides" tant que vous savez ce que vous faites et que vous êtes explicite à ce sujet (comme, en utilisant reinterpret_cast). Mais si vous liez un temporaire à une référence non constante, vous pouvez continuer à le faire circuler "pour toujours" juste pour que votre manipulation de l'objet disparaisse, car quelque part en cours de route, vous avez complètement oublié que c'était un temporaire.
Si j'étais vous, je repenserais la conception de mes fonctions. Pourquoi g () accepte-t-il une référence, modifie-t-il le paramètre? Si non, faites-le const référence, si oui, pourquoi essayez-vous de lui passer un temporaire, ne vous souciez-vous pas que ce soit un temporaire que vous modifiez? Pourquoi getx () est-il de retour temporaire de toute façon? Si vous partagez avec nous votre scénario réel et ce que vous essayez d'accomplir, vous pouvez obtenir de bonnes suggestions sur la façon de le faire.
Aller à l'encontre du langage et tromper le compilateur résout rarement les problèmes - généralement cela crée des problèmes.
Edit: Répondre aux questions dans le commentaire: 1)
X& x = getx().ref(); // OK when will x die?
- Je ne sais pas et je m'en fiche, car c'est exactement ce que je veux dire par "aller à l'encontre de la langue". Le langage dit que "les temporaires meurent à la fin de la déclaration, à moins qu'ils ne soient obligés de const const, auquel cas ils meurent lorsque la référence sort du cadre". En appliquant cette règle, il semble que x soit déjà mort au début de l'instruction suivante, car il n'est pas lié à la référence const (le compilateur ne sait pas ce que ref () renvoie). Ce n'est qu'une supposition cependant.2) J'ai clairement énoncé le but: vous n'êtes pas autorisé à modifier les temporaires, car cela n'a tout simplement pas de sens (en ignorant les références de valeur C ++ 0x). La question "alors pourquoi suis-je autorisé à appeler des membres non const?" est une bonne réponse, mais je n'ai pas de meilleure réponse que celle que j'ai déjà indiquée ci-dessus.
3) Eh bien, si j'ai raison à propos de x en
X& x = getx().ref();
mourant à la fin de la déclaration, les problèmes sont évidents.Quoi qu'il en soit, d'après votre question et vos commentaires, je ne pense pas que même ces réponses supplémentaires vous satisferont. Voici une dernière tentative / résumé: le comité C ++ a décidé qu'il n'était pas logique de modifier les temporaires, par conséquent, ils ont interdit la liaison à des références non const. Il peut y avoir une implémentation du compilateur ou des problèmes historiques ont également été impliqués, je ne sais pas. Ensuite, un cas spécifique a émergé, et il a été décidé que contre toute attente, ils permettraient toujours une modification directe en appelant la méthode non-const. Mais c'est une exception - vous n'êtes généralement pas autorisé à modifier les temporaires. Oui, C ++ est souvent aussi bizarre.
la source
int
etc.), mais il est permis pour les types définis par l' utilisateur:(std::string("A")+"B").append("C")
.g()
modifiait l'objet (que vous attendez d'une fonction prenant une référence non const), cela modifierait un objet qui va mourir, donc personne ne pourrait de toute façon obtenir la valeur modifiée. Il dit que c'est très probablement une erreur.Dans votre code
getx()
retourne un objet temporaire, un soi-disant "rvalue". Vous pouvez copier des valeurs r dans des objets (alias. Variables) ou les lier à des références const (ce qui prolongera leur durée de vie jusqu'à la fin de la vie de la référence). Vous ne pouvez pas lier rvalues à des références non const.Il s'agissait d'une décision de conception délibérée 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 d'abord faire une copie locale ou de l'objet ou le lier à une référence const:
Notez que la prochaine norme C ++ inclura des références rvalue. Ce que vous appelez des références devient donc des «références de valeur». Vous serez autorisé à lier rvalues aux références rvalue et vous pouvez surcharger les fonctions sur "rvalue-ness":
L'idée derrière les références rvalue est que, puisque ces objets vont mourir de toute façon, vous pouvez profiter de cette connaissance et implémenter ce qu'on appelle la "sémantique de déplacement", un certain type d'optimisation:
la source
g(getx())
ne fonctionne pas car sa signature estg(X& x)
etget(x)
renvoie un objet temporaire, donc nous ne pouvons pas lier un objet temporaire ( rvalue ) à une référence non constante, n'est -ce pas? Et dans votre premier morceau de code, je pense que ce seraconst X& x2 = getx();
au lieu deconst X& x1 = getx();
..:-/
Oui, votre raisonnement est correct, quoique un peu en arrière: nous ne pouvons pas lier les temporaires à desconst
références non (lvalue), et par conséquent le temporaire retourné pargetx()
(et nonget(x)
) ne peut pas être lié à la référence lvalue à laquelle l'argument fait référenceg()
.getx()
(et nonget(x)
) ?getx()
, et nonget(x)
(comme vous l'avez écrit).Ce que vous montrez, c'est que le chaînage d'opérateurs est autorisé.
L'expression est 'getx (). Ref ();' et ceci est exécuté jusqu'à son terme avant d'être affecté à «x».
Notez que getx () ne renvoie pas une référence mais un objet entièrement formé dans le contexte local. L'objet est temporaire mais il n'est pas const, vous permettant ainsi d'appeler d'autres méthodes pour calculer une valeur ou provoquer d'autres effets secondaires.
Regardez à la fin de cette réponse pour un meilleur exemple de cela
Vous ne pouvez pas lier un temporaire à une référence, car cela générera une référence à un objet qui sera détruit à la fin de l'expression, vous laissant ainsi une référence pendante (qui est désordonnée et la norme n'aime pas désordonnée).
La valeur retournée par ref () est une référence valide mais la méthode ne prête aucune attention à la durée de vie de l'objet qu'elle retourne (car elle ne peut pas avoir cette information dans son contexte). Vous venez de faire l'équivalent de:
La raison pour laquelle il est correct de le faire avec une référence const à un objet temporaire est que la norme étend la durée de vie du temporaire à la durée de vie de la référence afin que la durée de vie des objets temporaires soit prolongée au-delà de la fin de l'instruction.
La seule question qui reste à résoudre est donc la suivante: pourquoi la norme ne veut-elle pas autoriser une référence aux temporels pour prolonger la durée de vie de l'objet au-delà de la fin de l'énoncé?
Je pense que c'est parce que cela rendrait le compilateur très difficile à corriger pour les objets temporaires. Cela a été fait pour les références const aux temporaires car cela a une utilisation limitée et vous a donc forcé à faire une copie de l'objet pour faire quoi que ce soit d'utile mais fournit des fonctionnalités limitées.
Pensez à cette situation:
Prolonger la durée de vie de cet objet temporaire va être très déroutant.
Alors que ce qui suit:
Vous donnera un code intuitif à utiliser et à comprendre.
Si vous souhaitez modifier la valeur, vous devez renvoyer la valeur à une variable. Si vous essayez d'éviter le coût de la recopie de l'objet à partir de la fonction (car il semble que l'objet soit recopié (techniquement)). Alors ne vous embêtez pas, le compilateur est très bon en «optimisation de la valeur de retour»
la source
const_cast<x&>(getX());
n'a pas de sensPourquoi est discuté dans la FAQ C ++ (mine en gras ):
la source
x * 0
donnex
? Quoi? Quoi??incr(0);
de cette manière. De toute évidence, si cela était autorisé, il serait interprété comme créant un entier temporaire et le transmettant àincr()
Le principal problème est que
est une erreur logique:
g
modifie le résultat degetx()
mais vous n'avez aucune chance d'examiner l'objet modifié. S'ilg
n'avait pas besoin de modifier son paramètre, il n'aurait pas eu besoin d'une référence lvalue, il aurait pu prendre le paramètre par valeur ou par référence const.est valide car vous devez parfois 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
g(getx())
valider, ce que les concepteurs de langage essayaient d'éviter en premier lieu.est valide car les méthodes ne connaissent que la constance de la
this
, elles ne savent pas si elles sont appelées sur une lvalue ou sur une rvalue.Comme toujours en C ++, vous avez une solution de contournement pour cette règle mais vous devez signaler au compilateur que vous savez ce que vous faites en étant explicite:
la source
Il semble que la question initiale de savoir pourquoi cela n'est pas autorisé a reçu une réponse claire: "car il s'agit très probablement d'une erreur".
FWIW, je pensais montrer comment faire, même si je ne pense pas que ce soit une bonne technique.
La raison pour laquelle je veux parfois passer un temporaire à une méthode prenant une référence non constante est de jeter intentionnellement une valeur renvoyée par référence que la méthode appelante ne se soucie pas. Quelque chose comme ça:
Comme expliqué dans les réponses précédentes, cela ne se compile pas. Mais cela compile et fonctionne correctement (avec mon compilateur):
Cela montre simplement que vous pouvez utiliser la conversion pour mentir au compilateur. Évidemment, il serait beaucoup plus propre de déclarer et de passer une variable automatique inutilisée:
Cette technique introduit une variable locale inutile dans la portée de la méthode. Si, pour une raison quelconque, vous souhaitez empêcher son utilisation plus tard dans la méthode, par exemple pour éviter toute confusion ou erreur, vous pouvez le masquer dans un bloc local:
- Chris
la source
Pourquoi voudriez-vous jamais
X& x = getx();
? Il suffit d'utiliserX x = getx();
et de s'appuyer sur RVO.la source
g(getx())
plutôt queg(getx().ref())
g
va modifier quelque chose sur lequel vous ne pouvez plus mettre la main.La solution de contournement diabolique implique le mot-clé «mutable». En fait, le mal est laissé comme un exercice pour le lecteur. Ou voir ici: http://www.ddj.com/cpp/184403758
la source
Excellente question, et voici ma tentative d'une réponse plus concise (car beaucoup d'informations utiles sont dans les commentaires et difficiles à creuser dans le bruit.)
Toute référence liée directement à un temporaire prolongera sa durée de vie [12.2.5]. En revanche, une référence initialisée avec une autre référence ne le sera pas (même si c'est finalement la même temporaire). Cela a du sens (le compilateur ne sait pas à quoi se réfère finalement cette référence).
Mais cette idée est extrêmement déroutante. Par exemple
const X &x = X();
, le temporaire durera aussi longtemps que lax
référence, maisconst X &x = X().ref();
ne le sera PAS (qui sait ce qui aref()
réellement été renvoyé). Dans ce dernier cas, le destructeur de forX
est appelé à la fin de cette ligne. (Ceci est observable avec un destructeur non trivial.)Cela semble donc généralement déroutant et dangereux (pourquoi compliquer les règles sur la durée de vie des objets?), Mais il y avait probablement un besoin au moins pour les références const, donc la norme définit ce comportement pour eux.
Tous les temporels persistent jusqu'à la fin de la pleine expression. Pour les utiliser, cependant, vous avez besoin d'une astuce comme vous l'avez fait avec
ref()
. C'est légal. Il ne semble pas y avoir de bonne raison pour que le cerceau supplémentaire saute, sauf pour rappeler au programmeur qu'il se passe quelque chose d'inhabituel (à savoir, un paramètre de référence dont les modifications seront rapidement perdues).la source
"Il est clair que l'objet temporaire n'est pas constant dans l'exemple ci-dessus, car les appels à des fonctions non constantes sont autorisés. Par exemple, ref () pourrait modifier l'objet temporaire."
Dans votre exemple, getX () ne renvoie pas de const X, vous pouvez donc appeler ref () de la même manière que vous pourriez appeler X (). Ref (). Vous renvoyez une référence non constante et vous pouvez donc appeler des méthodes non const, ce que vous ne pouvez pas faire est d'affecter la référence à une référence non constante.
Avec le commentaire de SadSidos, cela rend vos trois points incorrects.
la source
J'ai un scénario que je voudrais partager où j'aimerais pouvoir faire ce que demande Alexey. Dans un plugin Maya C ++, je dois faire le shenanigan suivant afin d'obtenir une valeur dans un attribut de nœud:
C'est fastidieux à écrire, j'ai donc écrit les fonctions d'assistance suivantes:
Et maintenant, je peux écrire le code depuis le début du message comme:
Le problème est qu'il ne compile pas en dehors de Visual C ++, car il essaie de lier la variable temporaire renvoyée par le | à la référence MPlug de l'opérateur <<. Je voudrais que ce soit une référence car ce code est appelé plusieurs fois et je préfère que MPlug ne soit pas copié autant. J'ai seulement besoin de l'objet temporaire pour vivre jusqu'à la fin de la deuxième fonction.
Eh bien, c'est mon scénario. Je pensais juste montrer un exemple où l'on aimerait faire ce qu'Alexey décrit. J'accueille toutes les critiques et suggestions!
Merci.
la source