C++11 Range-basierte for-Loop-Effizienz „const auto &i“ versus „auto i“

Lesezeit: 5 Minuten

Benutzer-Avatar
Benutzer2052561

In C++11 kann ich so über einen Container iterieren:

for(auto i : vec){
   std::cout << i << std::endl;
}

Aber ich weiß, dass dies unnötig – unnötigerweiseda muss ich nur drucken die Werte von vec – erstellt eine Kopie von (BEARBEITEN: jedes Element von) vecalso könnte ich stattdessen Folgendes tun:

for(auto &i : vec){
   std::cout << i << std::endl;
}

Aber ich möchte sicherstellen, dass die Werte von vec werden nie geändert und halten sich an die Konstantenkorrektheit, also kann ich Folgendes tun:

for(const auto &i : vec){
   std::cout << i << std::endl;
}

Meine Frage ist also: Wenn ich nur muss suchen würde bei den Werten einiger Container nicht die allerletzte Schleife (const auto &i) immer bevorzugt werden, da es effizienter ist, keine zusätzliche Kopie von (BEARBEITEN: jedes Element von) vec?

Ich habe ein Programm, das ich entwickle, in dem ich erwäge, diese Änderung durchgehend vorzunehmen, da die Effizienz entscheidend ist (der Grund, warum ich C++ an erster Stelle verwende).

  • Ja, wenn Sie nur Lesezugriff auf ein Argument benötigen, sollte es durchgehen auto const& um eine unnötige Kopie zu vermeiden.

    – David G

    10. Juni 2013 um 20:33 Uhr


  • Das Schlüsselwort “const” macht Ihren Code jedoch nicht schneller …

    – Tim

    10. Juni 2013 um 20:35 Uhr

  • for (auto i : vec) macht keine zusätzliche Kopie des Ganzen veces kopiert jeden Element von vec hinein i.

    – Casey

    10. Juni 2013 um 20:59 Uhr


  • @Casey Ich verstehe. Bei jeder neuen Iteration wird also die vorherige Kopie gelöscht, richtig?

    – Benutzer2052561

    10. Juni 2013 um 21:07 Uhr

  • @ user2052561 – nicht gelöscht, aber zerstört.

    – Peter Becker

    10. Juni 2013 um 23:19 Uhr

Benutzer-Avatar
GManNickG

Ja. Aus dem gleichen Grund, wenn Sie immer nur ein Argument lesen, machen Sie den Parameter const&.

T        // I'm copying this
T&       // I'm modifying this
const T& // I'm reading this

Das sind Ihre “Standardwerte”. Wann T ist ein grundlegender Typ (eingebaut), auf den Sie jedoch im Allgemeinen einfach zurückgreifen const T (keine Referenz) zum Nachlesen, da eine Kopie billiger ist als Aliasing.


Ich habe ein Programm, das ich entwickle, bei dem ich erwäge, diese Änderung durchgehend vorzunehmen, da Effizienz entscheidend ist

  1. Nehmen Sie keine blinden Änderungen vor. Ein funktionierendes Programm ist besser als ein schnelles, aber kaputtes Programm.
  2. Wie Sie durch Ihre Schleifen iterieren, wird wahrscheinlich keinen großen Unterschied machen; Du loopst aus einem bestimmten Grund, nicht wahr? Der Körper Ihrer Schleife wird viel wahrscheinlicher der Schuldige sein.
  3. Wenn Effizienz entscheidend ist, möchten Sie a verwenden Profiler um herauszufinden, welche Teile Ihres Programms sind eigentlich langsam, anstatt Teile davon zu erraten könnte langsam sein. Siehe Nr. 2, warum Ihre Vermutung falsch sein könnte.

  • “Kopieren ist billiger als Aliasing” nur bei den meisten grundlegenden Typen, außerdem kann die Bewegungssemantik in der Situation des OP nicht verwendet werden, und da es sich um einen beliebigen Typ handeln kann, wäre eine Referenz die richtige Wahl.

    – Tim

    10. Juni 2013 um 20:40 Uhr

  • “Kopieren ist billiger als Aliasing” nur bei den grundlegendsten Typen” Aus diesem Grund ist die erste Hälfte dieses Satzes darauf ausgerichtet, zu sagen, dass er auf grundlegende Typen zutrifft …? Offensichtlich verwenden Sie den allgemeinen Fall, wenn Sie den Typ nicht kennen.

    – GManNickG

    10. Juni 2013 um 20:41 Uhr


  • Warum würde der Compiler beispielsweise in einer Range-Schleife unterschiedlichen Code für “int” und “const int &” generieren? Es macht einen Unterschied, wenn Argumente an eine Funktion übergeben werden, aber in einer Bereichsschleife hat der Compiler alles, was er braucht, um die Hölle daraus zu optimieren. Ich habe versucht, ein paar Bereichsschleifen zu zerlegen, und der generierte Code war derselbe. Gleiches gilt für Strukturen mit trivialem dtor und trivialem copy-ctor.

    – Sergio Martins

    21. Dezember 2015 um 22:55 Uhr


  • @SergioMartins: Das würde es nicht, deshalb solltest du es einfach verwenden const T&. Im Allgemeinen ist dies das, was Sie wollen, und es ist sinnlos, es weiter zu ändern in “const T” oder nur “T„Wenn du es zufällig weißt T; der Compiler erledigt dies für Sie.

    – GManNickG

    22. Dezember 2015 um 20:41 Uhr

  • mit const auto& Sie signalisieren, dass Sie nichts verändern wollen und auch keine Kopie benötigen. Selbst wenn die Leistung die gleiche wäre, würde ich mich dafür entscheiden, weil es Ihren Code ausdrucksstärker macht.

    – hochl

    3. Februar 2017 um 8:00 Uhr

Haftungsausschluss: Im Allgemeinen ist der Unterschied zwischen auto und auto& ist subtil, teilweise eine Frage des Stils, manchmal aber auch eine Frage der Korrektheit. Ich werde hier nicht den allgemeinen Fall behandeln!

In einer bereichsbasierten for-Schleife ist der Unterschied zwischen

for (auto element : container) {}

und

for (auto& element_ref : container) {}

ist dass element ist eine Kopie der Elemente in der containerwährend element_ref ist ein Verweis auf die Elemente im Container.

Betrachten Sie dieses Beispiel, um den Unterschied in der Aktion zu sehen:

#include <iostream>

int main(void) {
    int a[5] = { 23,443,16,49,66 };

    for (auto i : a) i = 5;       
    for (const auto& i : a) std::cout << i << std::endl;
    for (auto& i : a) i = 5;   
    for (const auto& i : a) std::cout << i << std::endl;    
}

Es wird gedruckt

23
443
16
49
66
5
5
5
5
5

weil die erste Schleife mit Kopien der Array-Elemente arbeitet, während die zweite tatsächlich die Elemente im Array ändert.

Wenn Sie die Elemente nicht ändern möchten, dann a const auto& ist besser geeignet, weil es das Kopieren der Elemente vermeidet (was teuer sein kann).

Stellen Sie sich vor, Ihr Vektor enthält Zeichenfolgen. Lange Saiten. 5000 lange Saiten. Wenn Sie sie unnötigerweise kopieren, erhalten Sie am Ende eine schön geschriebene for-Schleife, die schrecklich ineffizient ist.

Stellen Sie sicher, dass Ihr Code Ihrer Absicht folgt. Wenn Sie keine Kopie innerhalb der Schleife benötigen, erstellen Sie keine.

Verwenden Sie eine Referenz & wie oben vorgeschlagen oder Iteratoren.

1012860cookie-checkC++11 Range-basierte for-Loop-Effizienz „const auto &i“ versus „auto i“

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy