web-dev-qa-db-fra.com

Quel est l'avantage d'utiliser des références de transfert dans des boucles basées sur une plage?

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 reprises récemment. Cela me fait me demander:

Est-il possible que dans certains cas de coins obscurs, l'utilisation des références de transfert présente des avantages en termes de performances, par rapport à auto& ou const auto&?

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


Mise à jour Deux exemples que j'ai trouvés dans mes favoris:

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

Veuillez vous concentrer sur la question: pourquoi voudrais-je utiliser l'auto && dans la plage pour les boucles?

108
Ali

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

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto& e : v)
        e = true;
}

Cela ne compile pas car rvalue vector<bool>::reference renvoyé par iterator ne sera pas lié à une référence de valeur non constante. Mais cela fonctionnera:

#include <vector>

int main()
{
    std::vector<bool> 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 devez satisfaire un tel cas d'utilisation. C'est à dire. Je ne ferais pas cela gratuitement car cela fait amener 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 <vector>

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

Modifier

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

85
Howard Hinnant

L'utilisation de auto&& Ou références universelles avec une boucle for- basée sur une plage a l'avantage de capturer ce que vous obtenez. Pour la plupart des types d'itérateurs, 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 itérateur donne un temporaire: C++ 2011 a des exigences assouplies et les itérateurs ne sont pas nécessairement tenus de fournir une valeur l. L'utilisation de références universelles correspond à la transmission d'arguments dans std::for_each():

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

L'objet fonction f peut traiter T&, T const& Et T différemment. Pourquoi le corps d'une boucle for- 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:

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

Bien sûr, l'utilisation de std::forward() signifie que vous acceptez de déplacer toutes les valeurs renvoyées. Si des objets comme celui-ci ont beaucoup de sens dans le code non modèle, je ne 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 modèle, il ne prend pas de décision sur ce qui doit se passer avec les objets.

25
Dietmar Kühl

J'utilise pratiquement toujours auto&&. Pourquoi vous faire mordre par une affaire Edge alors que vous n'êtes pas obligé de le faire? Il 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.

7
Puppy