const auto&
suffirait si je veux effectuer des opérations en lecture seule. Cependant, je suis tombé sur
for (auto&& e : v) // v is non-const
quelques fois récemment. Cela me fait me demander:
Est-il possible que, dans certains cas obscurs, l'utilisation de références de transfert présente un avantage en termes de performances par rapport à auto&
ou const auto&
?
( shared_ptr
est un suspect pour les cas de coin obscurs)
Mettre à jour Deux exemples que j'ai trouvés dans mes favoris:
Un inconvénient de l'utilisation de la référence const lors de l'itération sur les types de base?
Puis-je facilement parcourir les valeurs d'une carte à l'aide d'une boucle for basée sur une plage?
Veuillez vous concentrer sur la question: pourquoi voudrais-je utiliser les boucles auto && basées sur la plage pour les boucles?
auto&&
des boucles for basées sur la plage?Réponses:
Le seul avantage que je peux voir est lorsque l'itérateur de séquence renvoie une référence proxy et que vous devez opérer sur cette référence d'une manière non const. Par exemple, considérez:
Cela ne compile pas car rvalue
vector<bool>::reference
renvoyée pariterator
ne sera pas liée à une référence lvalue non const. Mais cela fonctionnera:Cela étant dit, je ne coderais pas de cette façon à moins que vous ne sachiez que vous devez satisfaire un tel cas d'utilisation. C'est-à-dire que je ne ferais pas cela gratuitement parce que cela amène les gens à se demander ce que vous faites. Et si je le faisais, cela ne ferait pas de mal d'inclure un commentaire expliquant pourquoi:
Éditer
Ce dernier cas devrait vraiment être un modèle pour avoir du sens. Si vous savez que la boucle gère toujours une référence proxy,
auto
cela fonctionnera aussi bien queauto&&
. Mais lorsque la boucle traitait parfois des références non proxy et parfois des références proxy, je pense queauto&&
cela deviendrait la solution de choix.la source
const auto&
quand je veux que le compilateur m'aide à vérifier que je ne modifie pas accidentellement les éléments de la séquence.auto&&
dans du code générique où j'ai besoin de modifier les éléments de la séquence. Si je ne le fais pas, je m'en tiendraiauto const&
.L'utilisation de références universelles
auto&&
ou avec une boucle basée sur une plage présente l'avantage de capturer ce que vous obtenez. Pour la plupart des types d'itérateurs, vous obtiendrez probablement un ou un pour un type . Le cas intéressant est celui où le déréférencement d'un itérateur produit une valeur temporaire: C ++ 2011 a des exigences assouplies et les itérateurs ne sont pas nécessairement nécessaires pour produire une lvalue. L'utilisation de références universelles correspond à la transmission d'argument dans :for
T&
T const&
T
std::for_each()
L'objet de la fonction
f
peut traiterT&
,T const&
etT
différemment. Pourquoi le corps d'unefor
boucle basée sur une plage devrait-il être différent? Bien sûr, pour profiter réellement d'avoir déduit le type à l'aide de références universelles, vous devez les transmettre en conséquence:Bien sûr, utiliser
std::forward()
signifie que vous acceptez toutes les valeurs renvoyées à partir de. Je ne sais pas (encore?) Si des objets comme celui-ci ont beaucoup de sens dans un code non-modèle. J'imagine que l'utilisation de références universelles peut offrir plus d'informations au compilateur pour faire la bonne chose. Dans le code basé sur des modèles, il ne prend aucune décision sur ce qui devrait se passer avec les objets.la source
J'utilise pratiquement toujours
auto&&
. Pourquoi se faire mordre par un étui de bord alors que ce n'est pas nécessaire? C'est plus court à taper aussi, et je le trouve simplement plus ... transparent. Lorsque vous utilisezauto&& x
, vous savez quex
c'est exactement*it
, à chaque fois.la source
const
renoncez à l'être avecauto&&
si celaconst auto&
suffit. La question demande les cas d'angle où je peux me faire mordre. Quels sont les cas de coin qui n'ont pas encore été mentionnés par Dietmar ou Howard?auto&&
. Si le type que vous capturez doit être déplacé dans le corps de la boucle (par exemple), et est changé à une date ultérieure afin qu'il se résout en unconst&
type, votre code continuera de fonctionner en silence, mais vos mouvements seront des copies. Ce code sera très trompeur. Cependant, si vous spécifiez explicitement le type comme référence de valeur r, quiconque a changé le type de conteneur obtiendra une erreur de compilation car vous vouliez vraiment que ces objetsfor (std::decay_t<decltype(*v.begin())>&& e: v)
? Je suppose qu'il y a une meilleure façon ...auto&&
donnera une "référence universelle", donc toute autre chose que cela vous donnera un type plus spécifique. Siv
est supposé être avector
, vous pouvez le fairedecltype(v)::value_type&&
, ce que je suppose que vous vouliez en prenant le résultat deoperator*
sur un type d'itérateur. Vous pouvez égalementdecltype(begin(v))::value_type&&
vérifier les types de l'itérateur au lieu du conteneur. Si nous avons si peu d'informations sur le type, cependant, nous pourrions considérer qu'il est un peu plus clair d'aller avecauto&&
...