Question

Pourquoi n'est-il pas autorisé à obtenir une référence non contente à un objet temporaire, quelle fonction getx() Retour?De toute évidence, cela est interdit par la norme C ++, mais je m'intéresse à l'objectif 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, car une référence constante à un objet est pas interdit par la norme C++.
  2. 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.
  3. En outre, ref() 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 que "l'attribution d'un objet temporaire à la référence const prolonge la durée de vie de cet objet" et "rien n'est dit sur les références non const".Mon question supplémentaire.L'affectation suivante prolonge-t-elle la durée de vie de l'objet temporaire ?

X& x = getx().ref(); // OK
Était-ce utile?

La solution

De cette Visual C ++ article de blog sur les références rvalue :

  

... C ++ ne veut pas que vous accidentellement   modifier temporaires, mais directement   appeler une fonction membre non-const sur   un rvalue modifiable est explicite, donc   il est permis ...

En fait, vous ne devriez pas essayer de modifier la très temporaires parce qu'ils sont des objets temporaires et vont mourir d'un moment. La raison pour laquelle vous êtes autorisé à appeler des méthodes non-const est que, eh bien, vous êtes invités à faire des choses « stupides » aussi longtemps que vous savez ce que vous faites et vous êtes explicite à ce sujet (comme, en utilisant reinterpret_cast). Mais si vous liez temporaire à une référence non-const, vous pouvez continuer à passer autour « pour toujours » juste pour avoir votre manipulation de l'objet disparaît, parce que quelque part le long de la façon dont vous a complètement oublié c'était temporaire.

je serais repensons Si je vous étais, la conception de mes fonctions. Pourquoi est-g () accepter référence, ne modifie le paramètre? Si non, faire référence const, si oui, pourquoi vous essayez de passer temporairement à elle, ne te soucies c'est un que vous modifiez temporaire? Pourquoi GetX () 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 quelques bonnes suggestions sur la façon de le faire.

Allant à l'encontre de la langue et duper le compilateur résout rarement des problèmes -. En général, il crée des problèmes


Edit: répondre à des questions dans les commentaires: 1) X& x = getx().ref(); // OK when will x die? - Je ne sais pas et je ne me soucie pas, parce que c'est exactement ce que je veux dire par « aller à l'encontre de la langue ». La langue dit « meurent à la temporaires fin de l'instruction, à moins qu'ils soient liés à la référence const, auquel cas ils meurent lorsque la référence est hors de portée ». L'application de cette règle, il semble x est déjà mort au début de l'instruction suivante, car il est pas tenu de référence const (le compilateur ne sait pas ce qui rend ref ()). Ceci est juste une supposition cependant.

2) je l'ai dit clairement le but: vous n'êtes pas autorisé à modifier temporaires, car il ne fonctionne tout simplement pas de sens (en ignorant les références C ++ 0x rvalue). La question « pourquoi suis-je autorisé à appeler les membres non-const? » est un bon, mais je n'ai pas meilleure réponse que celle que j'ai déjà dit plus haut.

3) Eh bien, si je ne me trompe pas sur les x dans X& x = getx().ref(); meurent à la fin de la déclaration, les problèmes sont évidents.

Quoi qu'il en soit, en fonction de votre question et les commentaires, je ne pense même pas ces réponses supplémentaires vous satisfaire. Voici une tentative / résumé final: Le comité C ++ a décidé qu'il n'a pas de sens de modifier temporaires, par conséquent, ils refusées liaison aux références non-const. Peut-être quelques problèmes de mise en œuvre du compilateur ou historiques ont également été impliqués, je ne sais pas. Ensuite, certains cas précis émergé, et il a été décidé que, contre toute attente, ils vont encore permettre la modification directe par appel de la méthode non-const. Mais c'est une exception - vous généralement pas autorisé à modifier temporaires. Oui, C ++ est souvent bizarre.

Autres conseils

Dans ton code getx() renvoie un objet temporaire, appelé "rvalue".Vous pouvez copier des valeurs dans des objets (aka.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 des rvalues ​​à des références non const.

Il s'agit d'une décision de conception délibérée afin d'éviter que les utilisateurs ne modifient accidentellement un objet qui va mourir à la fin de l'expression :

g(getx()); // g() would modify an object without anyone being able to observe

Si vous souhaitez faire cela, vous devrez d'abord soit faire une copie locale de l'objet, soit le lier à une référence const :

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-const reference

Notez que la prochaine norme C++ inclura des références rvalue.Ce que vous appelez références devient donc désormais appelé « références lvalue ».Vous serez autorisé à lier des rvalues ​​à des références rvalue et vous pourrez surcharger des fonctions sur "rvalue-ness":

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

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

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 ces connaissances et implémenter ce qu'on appelle la « sémantique de déplacement », un certain type d'optimisation :

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

  data* pimpl; // you would use a smart ptr, of course
};


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

Ce que vous montrez que Enchaînement de l'opérateur est autorisé.

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

L'expression est 'GetX () ref ();.' et cela est exécuté à la fin avant l'affectation à « x ».

Notez que GetX () ne renvoie pas de référence, mais un objet entièrement formé dans le contexte local. L'objet est temporaire, mais il est pas const, ce qui vous permet d'appeler d'autres méthodes pour calculer une valeur ou avoir d'autres effets secondaires se produisent.

// 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 ce

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 ainsi une référence boiteuse (qui est en désordre et la norme n'aime pas en désordre).

La valeur retournée par ref () est une référence valide, mais la méthode ne paie aucune attention à la durée de vie de l'objet qu'elle retourne (car il ne peut pas avoir cette information dans son contexte). Vous avez fondamentalement juste fait l'équivalent de:

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

La raison pour laquelle il est correct de faire cela avec une référence const à un objet temporaire est que la norme prolonge la durée de vie du temporaire à la durée de vie de la référence pour la durée de vie des objets temporaires est prolongée au-delà de la fin de l'instruction.

Donc, la seule question qui reste est pourquoi la norme veut pas permettre référence à de prolonger la temporaires vie de l'objet au-delà de la fin de la déclaration?

Je crois qu'il est parce que cela rendrait le compilateur très difficile d'obtenir correct pour des objets temporaires. Il a été fait pour les références const à ce que vous provisoires a usage limité et donc forcé de faire une copie de l'objet à faire quelque chose d'utile, mais ne fournit 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.
Alors que les éléments suivants:

int const& y = getI();

vous donnera un code qu'il est intuitif à utiliser et à comprendre.

Si vous souhaitez modifier la valeur que vous devez renvoyer la valeur à une variable. Si vous essayez d'éviter le coût de la copie du obejct retour de la fonction (car il semble que l'objet est construit copie en arrière (il est techniquement)). Alors ne dérange pas le compilateur est très bon à « Valeur de retour Optimisation »

Pourquoi est discuté dans le FAQ C++ (en gras le mien):

En C++, les références non const peuvent se lier à des lvalues ​​et les références const peuvent se lier à des lvalues ​​ou des rvalues, mais rien ne peut se lier à une rvalue non const.C'est pour protéger les gens contre la modification des valeurs des temporaires qui sont détruits avant que leur nouvelle valeur puisse être utilisée.Par exemple:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

Si cet incr(0) était autorisé, soit un temporaire que personne n'a jamais vu serait incrémenté ou - bien pire - la valeur de 0 deviendrait 1.Ce dernier semble idiot, mais il y avait en fait un bug comme celui-là dans les premiers compilateurs Fortran qui réservaient un emplacement mémoire pour contenir la valeur 0.

Le principal problème est que

g(getx()); //error

est une erreur logique: g modifie le résultat de getx() mais vous n'avez pas la moindre chance d'examiner l'objet modifié. Si g n'a pas besoin de modifier son paramètre alors il ne serait pas nécessaire une référence lvalue, 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.

Toutefois, il est impossible de faire

X& x = getx(); // error

valide sans g(getx()) valide, qui est ce que les concepteurs de langage ont essayé d'éviter en premier lieu.

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

est valable parce que les méthodes ne connaissent la const-ness du this, ils ne savent pas s'ils sont appelés sur une lvalue ou sur un rvalue.

Comme toujours en C ++, vous avez une solution de contournement pour cette règle, mais vous devez signaler le compilateur que vous savez ce que vous faites en étant explicite:

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

On dirait que la question initiale à pourquoi ce n'est pas autorisé a été répondu clairement. « Parce qu'il est très probablement une erreur »

FWIW, je pensais que je viendrais comment il pourrait être fait, même si je ne pense pas que ce soit une bonne technique.

La raison pour laquelle je veux parfois passer temporaire à une méthode prenant une référence non-const est de jeter volontairement loin une valeur retournée par référence que 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, cela 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 que vous pouvez utiliser la coulée de mentir au compilateur. De toute évidence, il serait beaucoup plus propre de déclarer et de passer une variable automatique utilisé:

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

Cette technique n'introduit une variable locale non nécessaire dans le champ d'application de la méthode. Si pour une raison quelconque, vous voulez éviter d'être utilisé plus tard dans la méthode, par exemple, pour éviter toute confusion ou erreur, vous pouvez le cacher dans un bloc local:

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

- Chris

Pourquoi voudriez-vous jamais X& x = getx();? Il suffit d'utiliser X x = getx(); et compter sur RVO.

La solution mal implique le mot-clé « mutable ». En fait, le mal est être laissé comme un exercice pour le lecteur. Ou voir ici: http://www.ddj.com/cpp/184403758

Excellente question, et voici ma tentative de réponse plus concise (car de nombreuses informations utiles se trouvent dans les commentaires et sont difficiles à déterrer dans le bruit.)

Toute référence liée directement à un testament temporaire prolongera sa durée de vie [12.2.5].En revanche, une référence initialisée avec une autre référence sera pas (même si c'est finalement le même temporaire).Cela a du sens (le compilateur ne sait pas à quoi fait finalement référence cette référence).

Mais toute cette idée est extrêmement confuse.Par exemple. const X &x = X(); fera durer le temporaire aussi longtemps que le x référence, mais const X &x = X().ref(); NE le fera PAS (qui sait quoi ref() effectivement revenu).Dans ce dernier cas, le destructeur de X 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 est probable qu'il y avait au moins un besoin de références const, donc la norme définit ce comportement pour eux.

[Depuis sbi commentaire]:Notez que le fait que la liaison à une référence de const améliore les durées de vie d'un temporaire est une exception qui a été ajoutée délibérément (TTBOMK afin d'autoriser des optimisations manuelles).Il n'y a pas une exception ajoutée pour les références non-Const, car la liaison d'une référence temporaire à une référence non-conforme a été considérée comme une erreur du programmeur.

Tous les temporaires persistent jusqu'à la fin de l'expression complète.Cependant, pour les utiliser, vous avez besoin d'une astuce comme celle que vous avez avec ref().C'est légal.Il ne semble pas y avoir de bonne raison pour que le cadre supplémentaire soit franchi, 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).

[Un autre sbi Commentaire] La raison pour laquelle Stroustrup donne (en D&E) pour interdire la liaison des RValues ​​à des références non conformes est que, si G ()'s G () modifierait l'objet (que vous attendez d'une fonction prenant une référence non-conforte), Il modifierait un objet qui va mourir, afin que personne ne puisse quand même obtenir la valeur modifiée.Il dit que cela, très probablement, est une erreur.

"Il est clair que l'objet temporaire n'est pas constante dans l'exemple ci-dessus, parce que les appels à des fonctions non constantes sont autorisées. Par exemple, ref () pourrait modifier le temporaire objet. "

Dans votre exemple getX () ne renvoie pas de const X de sorte que vous pouvez appeler ref () de la même manière que l'on pourrait appeler X () réf. (). Vous retournerez un ref non const et peut donc appeler des méthodes non const, ce que vous ne pouvez pas faire est assigner l'arbitre à une référence non const.

En plus de SadSidos commenter ce qui rend vos trois points incorrects.

J'ai un scénario que je voudrais partager où je voudrais pouvoir faire ce que Alexey demande. Dans un plugin de Maya C, je dois faire la shenanigan suivante afin d'obtenir une valeur dans un attribut de noeud:

MFnDoubleArrayData myArrayData;
MObject myArrayObj = myArrayData.create(myArray);   
MPlug myPlug = myNode.findPlug(attributeName);
myPlug.setValue(myArrayObj);

Ceci est fastidieux d'écrire, donc je l'ai écrit les fonctions d'aide suivantes:

MPlug operator | (MFnDependencyNode& node, MObject& attribute){
    MStatus status;
    MPlug returnValue = node.findPlug(attribute, &status);
    return returnValue;
}

void operator << (MPlug& plug, MDoubleArray& doubleArray){
    MStatus status;
    MFnDoubleArrayData doubleArrayData;
    MObject doubleArrayObject = doubleArrayData.create(doubleArray, &status);
    status = plug.setValue(doubleArrayObject);
}

Et maintenant, je peux écrire le code depuis le début du poste:

(myNode | attributeName) << myArray;

Le problème est qu'il ne compile pas en dehors de Visual C ++, car il essaie de lier la variable temporaire retour de la | opérateur à la référence Mplug du << opérateur. Je voudrais que ce soit une référence parce que ce code est appelé plusieurs fois et je préfère ne pas avoir Mplug être copiés tant. Je ne référençant l'objet temporaire pour vivre jusqu'à la fin de la seconde fonction.

Eh bien, ceci est mon scénario. Juste pensé que je montre un exemple où l'on voudrait faire ce que Alexey décrire. Je salue toutes les critiques et suggestions!

Merci.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top