33 votes

Quel inconvénient d'utiliser la référence const lors de l'itération sur les types de base?

Je trouve moi-même à l'aide de C++11 de plus en plus tardivement, et où j'aurais été en utilisant des itérateurs dans le passé, je suis maintenant à l'aide de gamme à base de boucles à chaque fois que possible:

std::vector<int> coll(10);
std::generate(coll.begin(), coll.end(), []() { return rand(); } );

C++03:

for (std::vector<int>::const_iterator it = coll.begin(); it != coll.end(); ++it) {
   foo_func(*it);
}

C++11:

for (auto e : coll) { foo_func(e); }

Mais que faire si la collecte type d'élément est un paramètre du modèle? foo_func() sera probablement surchargé de passe complexe (= cher pour copier) de type const référence, simples et par valeur:

foo_func(const BigType& e) { ... };
foo_func(int e) { ... };

Je n'ai pas donné cette pensée bien alors que j'ai été en utilisant le C++03-style code ci-dessus. Je voudrais réitérer la même manière, et depuis référence à un const_iterator produit const référence, tout allait bien. Mais en utilisant le C++11 basés sur la plage pour la boucle, j'ai besoin d'utiliser un const de référence de la variable de boucle d'obtenir le même comportement:

for (const auto& e : coll) { foo_func(e); }

Et tout à coup je n'étais pas sûr, si ce ne serait pas inutile d'introduire des instructions de montage si auto était un type simple (comme un behind-the-scene pointeur de mettre en œuvre la référence).

Mais la compilation d'un exemple d'application a confirmé qu'il n'y a pas de frais généraux pour les types simples, et que cela semble être la façon générique à l'utilisation de la gamme, à base de boucles dans les modèles. Si ce n'avait pas été le cas, boost::call_traits::param_type aurait été le chemin à parcourir.

Question: existe-il des garanties dans la norme?

(Je me rends compte que le problème n'est pas vraiment liée à la gamme de base pour les boucles. Il est également présent lors de l'utilisation de const_iterators.)

13voto

Dietmar Kühl Points 70604

Le conteneur standard de renvoyer toutes les références de leur itérateur (à noter, cependant, que certains des "conteneurs ne sont pas vraiment conteneur, par exemple, std::vector<bool> qui retourne un proxy). D'autres itérateurs peuvent retourner des procurations ou des valeurs bien que ce n'est pas strictement pris en charge.

Bien sûr, la norme ne donne aucune garantie quant à la performance. Toute sorte de performance liés à la fonction (au-delà de la complexité des garanties) sont considérés comme la qualité de la mise en œuvre.

Cela dit, vous pourriez envisager d'avoir le compilateur de faire le choix pour vous comme il l'a fait avant:

for (auto&& e: coll) { f(e); }

Le principal problème ici est que l' f() peut recevoir un non-const de référence. Cela peut être évité en utilisant si nécessaire une const version coll.

11voto

Steve Jessop Points 166970

6.5.4/1 dit:

for ( for-range-declaration : braced-init-list ) statement

laissez-gamme-init être équivalent à l'embrassa-init-liste. Dans chaque cas, une la gamme de base pour l'énoncé est équivalent à

{
    auto && __range = range-init;
    for ( auto __begin = begin-expr,
                __end = end-expr;
            __begin != __end;
            ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

(plus d'explication suivante de la signification de tout ce qui __ gubbins).

Le standard n'a pas de garanties si la ligne const auto &e = *__begin introduit une surcharge de performance, bien sûr, par rapport directement à l'aide de *__begin au lieu de e à l'intérieur de l'énoncé. Les implémentations sont autorisés à mettre en œuvre des références par laborieusement de la copie d'un pointeur dans certains pile de logement et de le lire à chaque fois la référence est utilisée, et ne sont pas requis pour optimiser.

Mais il n'y a aucune raison pourquoi il devrait y avoir une surcharge dans un bon compilateur, dans le cas où l' __begin est un conteneur d'itérateur (dont operator* renvoie une référence), puis e est passé par valeur dans la déclaration.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X