Wie kann man in C++ über nicht konstante Variablen iterieren?

Wie kann man in C++ über nicht konstante Variablen iterieren?

Es funktioniert nicht, weil in {a,b} Sie erstellen eine Kopie von a und b . Eine mögliche Lösung wäre, die Schleifenvariable zu einem Zeiger zu machen, der die Adressen von a nimmt und b :

#include <initializer_list>

struct Obj {
    int i;
};

Obj a, b;

int main() {
    for(auto obj : {&a, &b}) {
        obj->i = 123;   
    }
}

Live ansehen

Hinweis:Es ist im Allgemeinen besser, auto zu verwenden , da es stille implizite Konvertierungen vermeiden könnte


Der Grund, warum das nicht funktioniert, ist, dass die zugrunde liegenden Elemente der std::initializer_list werden von a kopiert und b , und sind vom Typ const Obj , also versuchen Sie im Wesentlichen, einen konstanten Wert an eine veränderliche Referenz zu binden.

Man könnte versuchen, dies zu beheben, indem man verwendet:

for (auto obj : {a, b}) {
    obj.i = 123;
}

würde dann aber bald merken, dass die tatsächlichen werte von i in den Objekten a und b hat sich nicht geändert. Der Grund ist, dass bei Verwendung von auto hier der Typ der Schleifenvariable obj wird zu Obj , also durchlaufen Sie einfach Kopien von a und b .

Die tatsächliche Art und Weise, wie dies behoben werden sollte, ist, dass Sie std::ref verwenden können (definiert in <functional> Header), damit die Elemente in der Initialisierungsliste vom Typ std::reference_wrapper<Obj> sind . Das ist implizit in Obj& umwandelbar , also können Sie das als Typ der Schleifenvariable beibehalten:

#include <functional>
#include <initializer_list>
#include <iostream>

struct Obj {
    int i;
};

Obj a, b;

int main()
{
    for (Obj& obj : {std::ref(a), std::ref(b)}) { 
        obj.i = 123;
    }
    std::cout << a.i << '\n';
    std::cout << b.i << '\n';
}

Eine alternative Möglichkeit, das Obige zu tun, wäre, die Schleife const auto& verwenden zu lassen und std::reference_wrapper<T>::get . Wir können hier eine konstante Referenz verwenden, weil die reference_wrapper wird nicht verändert, nur der umschlossene Wert:

for (const auto& obj : {std::ref(a), std::ref(b)}) { 
    obj.get().i = 123;
}

aber ich denke das, weil ich auto verwende erzwingt hier die Verwendung von .get() , dies ist ziemlich umständlich und die erstere Methode ist der bevorzugte Weg, dies zu lösen.

Es scheint einfacher zu sein, dies zu tun, indem Sie rohe Zeiger in der Schleife verwenden, wie es @francesco in seiner Antwort getan hat, aber ich habe die Angewohnheit, rohe Zeiger so weit wie möglich zu vermeiden, und in diesem Fall glaube ich nur, dass die Verwendung von Referenzen macht der Code klarer und sauberer.


Beim Kopieren von a und b das gewünschte Verhalten ist, können Sie anstelle einer Initialisierungsliste ein temporäres Array verwenden:

#include <initializer_list>

struct Obj {
    int i;
} a, b;

int main() {
    typedef Obj obj_arr[];
    for(auto &obj : obj_arr{a, b}) {
        obj.i = 123;   
    }
}

Dies funktioniert auch, wenn Obj hat nur einen Bewegungskonstruktor.