Quel est l’avantage d’utiliser des références universelles dans les boucles basées sur les plages?

const auto& suffirait si je voulais effectuer des opérations en lecture seule. Cependant, je suis tombé sur

 for (auto&& e : v) // v is non-const 

deux fois récemment. Cela me fait me demander:

Est-il possible que, dans certains cas obscurs, l’utilisation de références universelles présente un certain avantage en termes de performances, par rapport à auto& ou const auto& ?

( shared_ptr est un suspect pour les cas de coin obscurs)


Mise à jour Deux exemples trouvés dans mes favoris:

Un inconvénient de l’utilisation de la référence const lors d’une itération sur des types de base?
Puis-je parcourir facilement les valeurs d’une carte en utilisant une boucle basée sur une plage?

Veuillez vous concentrer sur la question suivante: pourquoi voudrais-je utiliser les fonctions auto && pour les boucles basées sur la plage?

Le seul avantage que je peux voir est lorsque l’iterator de séquence renvoie une référence de proxy et que vous devez opérer sur cette référence de manière non constante. Par exemple, considérez:

 #include  int main() { std::vector v(10); for (auto& e : v) e = true; } 

Cela ne comstack pas car le vector::reference rvalue vector::reference renvoyé par l’ iterator ne se lie pas à une référence de lvalue non-const. Mais cela fonctionnera:

 #include  int main() { std::vector v(10); for (auto&& e : v) e = true; } 

Cela étant dit, je ne coderais pas de cette façon à moins que vous ne sachiez que vous deviez satisfaire à un tel cas d’utilisation. C’est-à-dire que je ne le ferais pas 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:

 #include  int main() { std::vector v(10); // using auto&& so that I can handle the rvalue reference // returned for the vector case for (auto&& e : v) e = true; } 

modifier

Ce dernier cas de ma part devrait vraiment être un modèle pour faire sens. Si vous savez que la boucle gère toujours une référence de proxy, alors auto fonctionnerait aussi bien que auto&& . Mais lorsque la boucle traitait parfois des références non-proxy et parfois des références de proxy, alors je pense que auto&& deviendrait la solution de choix.

L’utilisation de références auto&& ou universelles avec une plage basée for -loop présente l’avantage de capturer ce que vous obtenez. Pour la plupart des iterators, vous obtiendrez probablement un T& ou un T const& pour certains types T Le cas intéressant est celui où le déréférencement d’un iterator donne un caractère temporaire: les exigences de C ++ 2011 ont été assouplies et les iterators n’ont pas nécessairement besoin de générer une lvalue. L’utilisation de références universelles correspond à l’argument de transfert dans std::for_each() :

 template  F std::for_each(InIt it, InIt end, F f) { for (; it != end; ++it) { f(*it); // < ---------------------- here } return f; } 

L'object fonction f peut traiter T& , T const& , et T différemment. Pourquoi le corps d'une plage basée for -loop devrait-il être différent? Bien entendu, pour tirer profit de la déduction du type en utilisant des références universelles, vous devez les transmettre en conséquence:

 for (auto&& x: range) { f(std::forward(x)); } 

Bien sûr, l'utilisation de std::forward() signifie que vous acceptez que les valeurs renvoyées soient déplacées. Si des objects comme celui-ci ont beaucoup de sens dans le code non-template, je ne le sais pas (encore?). Je peux imaginer 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 doit se passer avec les objects.

J’utilise presque toujours auto&& . Pourquoi se faire piquer par un casse-tête lorsque vous n’avez pas à le faire? C’est plus court à taper aussi, et je le trouve simplement plus … transparent. Lorsque vous utilisez auto&& x , alors vous savez que x est exactement *it , à chaque fois.