9
votes

empêcher Pass-by-ref de l'objet temporaire

J'ai une classe qui "se souvient" une référence à un objet (par exemple une variable entière). Je ne peux pas y avoir de référence une valeur qui est détruite immédiatement et je cherche un moyen de protéger les utilisateurs de ma classe de le faire par accident.

est une surcharge de référence de RValue un bon moyen d'empêcher une être transmis? p>

 struct HasRef {
   int& a;
   HasRef( int& a ):a(a){}
   void foo(){ a=1; }
 private: 
   HasRef( int&& a );
 };

 ... HasRef r2(x+1); // doesn't compile => problem solved?


9 commentaires

Un temporaire ne lie pas à une référence de lvalue. La définition de R2 dans votre premier exemple ne doit pas compiler.


Si vous utilisez VC ++, une solution consiste à augmenter le niveau d'avertissement et il vous dira quand cela ne fonctionne pas.


Cependant, une référence const se lierait à une temporaire, la question est donc encore très bonne. J'ai examiné cette approche, mais je suis toujours d'avis que si une classe va stocker une référence (ou un pointeur) à l'objet référencé, il est préférable de prendre un pointeur dans le constructeur, de faire des préoccupations de vie potentielles Peu plus évident (quand un constructeur prend un pointeur, cela me fait penser à deux fois sur ce que l'objet va faire avec elle).


@ MusiPhil, @ Dave: En effet; J'ai distillé cette classe de quelque chose de mon travail de jour qui a provoqué un crash dans VS10, mais a couru en douceur dans GCC4.4; fournira un meilleur code demain.


@Jamesmcnellis: Mais si c'est une référence Const, il prolonge la durée de vie du temporaire, donc ce n'est plus un problème.


@Benjaminlindley: Non, il étend la durée de vie du temporaire seulement jusqu'à ce que la construction se termine. Une fois que l'objet est construit, A fait référence à un objet non plus existant.


Bien sûr, j'aurais dû dire "un temporaire ne se lie pas à une référence de lvalue non constituée". :-)


@xtofl, pouvez-vous trouver d'autres questions pour lesquelles le transbord-by-rvalue-référence tag pourrait appliquer? Il semble exceptionnellement spécifique à cette question, et ce n'est pas vraiment un bon cas pour une nouvelle étiquette.


@Chals: C'est en effet une nouvelle étiquette. C'est un concept assez nouveau aussi. Peut-être qu'il y a des tags pour «transfert», etc., mais je n'ai pas essayé de le chercher. Je vais creuser dedans.


4 Réponses :


2
votes

Cela ne devrait pas compiler. Un bon compilateur C ++ (ou vraiment presque n'importe quel compilateur C ++ que j'ai jamais vu) arrêtera cela de se produire.


1 commentaires

Merci; Il semble que je dois mieux regarder mon code et reformuler le problème.



3
votes

Ignorer le fait que le code n'est pas valide et ne répond que de la question sur la surcharge privée ...

en C ++ 11 Je préférerais une fonction supprimée à une fonction privée. C'est un peu plus explicite que vous vraiment ne pouvez pas l'appeler (pas même si vous êtes membre ou ami de la classe.)

n.b. Si le constructeur supprimé est hasidef (int &&) = Supprimer Il ne sera pas choisi ici: xxx

avec un argument de type const int && Le constructeur Hasref (const int &) serait utilisé, pas le hasidef (int &&) un. Dans ce cas, tout irait, car i est vraiment une lvalue, mais en général, ce n'est peut-être pas le cas, cela pourrait donc être l'un des temps très rares quand un const La référence de la rvalue est utile: xxx


0 commentaires

0
votes

Je suppose que vous compilez dans les MSVS. Dans ce cas, désactivez les extensions de langue et vous devriez obtenir une erreur.

Sinon, non, pas que même marquer la référence const étend la durée de vie du temporaire jusqu'à la fin du constructeur. Après cela, vous vous référerez à un objet invalide.


4 commentaires

Il est plus facilement dit que fait. La désactivation des extensions de langues avec MSVC rend leur propre bibliothèque standard.


@Alexandrec. Je n'ai eu aucun problème jusqu'à présent ... chanceux?


Probablement. Je me rappelle avoir eu des problèmes avec MSVC2005.


Je crois comprendre que les en-têtes de bibliothèque standard doivent travailler sous / ZA, mais les en-têtes Windows SDK (Windows.h et Amis) ne peuvent pas.



6
votes

Si vous devez stocker une référence const à une instance de type B dans votre classe a , alors vous voulez sûrement être assuré , la durée de vie de l'instance A sera dépassée par la durée de vie de B instance: xxx

pour le rendre possible que vous devriez explicitement < Code> = Supprimer; Toutes les surcharges du constructeur, qui peuvent accepter des rvalues ​​( const && et && ). Pour que cela vous réalise devrait juste à = Supprimer; uniquement const && version du constructeur. xxx

Cette approche permet Vous devez interdire la transmission du constructeur toutes sortes de rvalues.

Cela fonctionne également pour des scalaires intégrés. Par exemple, double const f () {retour 0,01; } , bien qu'il cause un avertissement comme:

AVERTISSEMENT: le qualificatif de type 'const' sur le type de retour n'a aucun effet [-Dimificateurs-qualificateurs]

peut toujours avoir effet si vous venez de = Supprimer; seulement && version du constructeur: xxx

Pour les constructeurs non conversions (c.-à-d. Non-unaire) Il existe un problème: vous devrez peut-être fournir = supprimer des versions; -D pour toutes les versions combinatoires possibles des constructeurs comme suit: xxx

interdire les cas mélangés tels que: xxx


2 commentaires

Bon point, les constructeurs multi-argers! Il serait probablement judicieux de rendre tous les paramètres modèles, et affirmer statiquement toutes les références de LValue en quelque sorte. Cela empêcherait la mix-explosion.


@xtofl Simple static_assert n'est pas bon choix, lorsque vous appliquez un type de type comme std :: is_constructible à A . Vous devez avoir des combinaisons de mauvaises combinaisons ou pour utiliser concept Lite si disponible.