262 votes

Comment se fait-il une référence non const ne peuvent pas attacher un objet temporaire ?

Pourquoi n'est-il pas permis d'obtenir des non-const référence à un objet temporaire, la fonction getx() retours? 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;
}
  1. 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 qui ne sont pas interdites par le C++ Standard.
  2. 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.
  3. 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". Ma question supplémentaire. Ne affectation suivante prolonger la durée de vie de l'objet temporaire?

X& x = getx().ref(); // OK

113voto

sbk Points 4987

À partir de ce Visual C++ article de blog sur les références rvalue:

... Le C++ ne veut pas que vous accidentellement modifier temporaires, mais directement l'appel d'une non-const fonction de membre sur modifiable rvalue est explicite, donc c'est autorisé ...

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.

Allant à 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) eh Bien, si je suis à droite sur x en 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.

49voto

sbi Points 100828

Dans votre code getx() renvoie un objet temporaire, un soi-disant "rvalue". Vous pouvez copier des rvalues dans "lvalues" (essentiellement des objets nommés, aka 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 une volonté délibérée de décision de conception afin d'empêcher les utilisateurs de modifier accidentellement un objet qui va mourir à la fin de l'expression:

// this doesn't compile: 
g(getx()); // g() would modify an object without anyone being able to observe

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:

X x1 = getx();
const X& x1 = getx();
g(x1); // fine
g(x2); // fine, too

Notez que la prochaine C++ standard comprendra références rvalue. Ce que vous connaissez des références est donc devenir à ê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":

void g(X&); // #1
void g(X&&); // #2, takes an rvalue reference

X x; 
g(x); // calls #1
g(getx()); // calls #2
g(X()); // calles #2, too

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:

X::X(X&& rhs)
  : pimpl( rhs.pimpl ) // steal rhs' data...
{
  rhs.pimpl = NULL; // ...and leave it deconstructible, but empty
}


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty

20voto

Loki Astari Points 116129

Ce que vous montrez, c'est que l'opérateur de chaînage est autorisé.

 X& x = getx().ref(); // OK

L'expression est " getx().ref (); "et cela est exécuté à l'accomplissement avant l'affectation à "x".

Notez 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.

// It would allow things like this.
getPipeline().procInstr(1).procInstr(2).procInstr(3);

// or more commonly
std::cout << getManiplator() << 5;

Regardez à la fin de cette réponse pour un meilleur exemple de cette

Vous pouvez pas lier un 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:

x& = const_cast<x&>(getX());

La raison, c'est OK de 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.

Pensez à cette situation:

int getI() { return 5;}
int x& = getI();

x++; // Note x is an alias to a variable. What variable are you updating.

L'extension de la durée de vie de cet objet temporaire va être très déroutant.
Tandis que le suivant:

int const& y = getI();

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 Optimisation"

6voto

Dan Berindei Points 2326

Le principal problème est que

g(getx()); //error

est une erreur de logique: g , c'est de modifier le résultat de l' getx() mais vous n'avez aucune chance d'examiner l'objet modifié. Si g 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.

const X& x = getx(); // OK

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

X& x = getx(); // error

valable sans faire g(getx()) valides, c'est ce que la langue concepteurs ont essayé d'éviter en premier lieu.

g(getx().ref()); //OK

est valide parce que les méthodes ne connais que la const-ness de l' 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:

g(const_cast<x&>(getX()));

6voto

Chris Pearson Points 96

Semble comme l'original question de savoir 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 il 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:

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

Comme expliqué dans les réponses précédentes, qui ne compile pas. Mais cette compile et fonctionne correctement (avec mon compilateur):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

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:

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

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:

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

-- Chris

Prograide.com

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.

Powered by:

X