Korrekte Verwendung der bereichsbasierten for-Schleife von C++11 in Qt

Lesezeit: 5 Minuten

Benutzer-Avatar
Auferstehung

Entsprechend dieses Gespräch Bei der Verwendung von C++11 Range Base gibt es einen gewissen Fallstrick for auf Qt-Containern. Prüfen:

QList<MyStruct> list;

for(const MyStruct &item : list)
{
    //...
}

Der Fallstrick, so der Vortrag, liegt im impliziten Teilen. Unter der Haube der Ferngesteuerte for holt den Iterator aus dem Container. Aber da ist der Container nicht const der Iterator wird nicht-const und das reicht anscheinend aus, damit sich der Behälter löst.

Wenn Sie die Lebensdauer eines Containers kontrollieren, ist dies einfach zu beheben, man übergibt das einfach const Verweis auf den Container, um dessen Verwendung zu erzwingen const_iterator und nicht zu lösen.

QList<MyStruct> list;
const Qlist<MyStruct> &constList = list;

for(const MyStruct &item : constList)
{
    //...
}

Aber was ist mit for Beispiel Container als Rückgabewerte.

QList<MyStruct> foo() { //... }

void main()
{
    for(const MyStruct &item : foo())
    {
    }
}

Was passiert hier? Wird der Container noch kopiert? Intuitiv würde ich sagen, es ist so, um zu vermeiden, dass dies möglicherweise getan werden muss?

QList<MyStruct> foo() { //... }

main()
{ 
    for(const MyStruct &item : const_cast<const QList<MyStruct>>(foo()))
    {
    }
}

Ich bin nicht sicher. Ich weiß, es ist ein bisschen ausführlicher, aber ich brauche das, weil ich auf großen Containern häufig bereichsbasierte for-Schleifen verwende, also hat das Gespräch bei mir irgendwie die richtige Saite getroffen.

Bisher verwende ich eine Hilfsfunktion, um den Container in die zu konvertieren const Referenz, aber wenn es einen kürzeren/einfacheren Weg gibt, dasselbe zu erreichen, würde ich es gerne hören.

  • Hör auf, dir darüber Sorgen zu machen. Alle Qt-Container implementieren das COW-Muster. Und in den neuesten Versionen implementiert das Qt-Team die Unterstützung von C++11, einschließlich Move-Ctors.

    – Dmitri Sasonow

    5. März 2016 um 8:01 Uhr

  • Übrigens, versuche es const MyStruct& const item : foo() im const-Stil iterieren.

    – Dmitri Sasonow

    5. März 2016 um 8:02 Uhr

  • @SaZ Ich werde deinen Vorschlag ausprobieren. Aber in Bezug auf COW sagte der Qt-Entwickler im verlinkten Vortrag ausdrücklich, dass das Erstellen eines nicht konstanten Iterators aus einem Container bedeutet, dass er sich löst. Es ist sinnvoll, weil sie sonst nicht erkennen könnten, ob Sie diesen Iterator tatsächlich verwendet haben, um ihn zu ändern, einfach die Tatsache, dass Sie es können, reicht aus.

    – Auferstehung

    5. März 2016 um 8:49 Uhr

  • Ich hatte buchstäblich nie ein Problem damit, einfach zu tun for(const auto& bla : blas) Ich sehe nicht, dass es sogar ein Problem damit geben könnte

    – Wütende Ente

    18. März 2016 um 12:00 Uhr

  • Sollte es nicht sein const QList<MyStruct> &constList = list; anstatt Qlist<MyStruct> &constList = list; um konstante Iteratoren zu erhalten und das Ablösen zu verhindern? Wenn nein, warum nicht?

    – vb

    4. November 2016 um 8:43 Uhr

Benutzer-Avatar
Yakk – Adam Nevraumont

template<class T>
std::remove_reference_t<T> const& as_const(T&&t){return t;}

könnte helfen. Ein implizit gemeinsam genutztes Objekt, das einen Rvalue zurückgibt, kann implizit Write-Shraring (und Detatch) aufgrund einer nicht konstanten Iteration erkennen.

Das gibt Ihnen:

for(auto&&item : as_const(foo()))
{
}

wodurch Sie konstant (und ziemlich klar) iterieren können.

Wenn Sie eine Verlängerung der Referenzlebensdauer benötigen, um zu funktionieren, haben Sie zwei Überladungen:

template<class T>
T const as_const(T&&t){return std::forward<T>
template<class T>
T const& as_const(T&t){return t;}

Aber über const rvalues ​​zu iterieren und sich darum zu kümmern, ist oft ein Designfehler: Sie sind Wegwerfkopien, warum spielt es eine Rolle, ob Sie sie bearbeiten? Und wenn Sie sich basierend auf der konstanten Qualifikation ganz anders verhalten, wird Sie das an anderer Stelle beißen.

  • Es stellt sich heraus, dass man zwei Überladungen von as_const benötigt, eine mit l-Wert (T&)-Referenz (wie angegeben) und die andere für r-Wert (T&&)-Referenzen (wie im Beispiel tatsächlich erforderlich). Beide haben natürlich den gleichen Rückgabewert und Inhalt.

    – Auferstehung

    5. März 2016 um 8:35 Uhr

  • @Auferstehung Ups. Beachten Sie jedoch, dass dies mit einer Überladung wie oben beschrieben möglich ist.

    – Yakk – Adam Nevraumont

    5. März 2016 um 8:49 Uhr

  • Der Grund, warum ich es auch für Temporäre haben möchte, ist einerseits der Vollständigkeit halber und andererseits wegen der impliziten gemeinsamen Nutzung (COW) in Qt. Grundsätzlich ist das Erstellen von flachen Kopien schnell und billig, aber in dem Moment, in dem Sie einen nicht konstanten Iterator erzeugen, führen Sie eine tiefe Kopie durch. Qt-Klassen geben Container oft nach Wert zurück, weil es billig ist. Aber wenn Sie sie nicht konstant durchlaufen, erstellen Sie die tiefe Kopie. Wenn Sie es nicht brauchen, wäre es eine Verschwendung (und manchmal bedeutsam), eine tiefe Kopie zu erstellen, um eine const for-range-Schleife auszuführen … Aber vielleicht verstehe ich es falsch. 🙂 Trotzdem vielen Dank für den Vorwärtstrick!

    – Auferstehung

    18. März 2016 um 15:07 Uhr

  • Neuere Qt-Versionen haben qAsConstsehen doc-snapshots.qt.io/qt5-dev/qtglobal.html#qAsConst.

    – Thomas McGuire

    18. März 2016 um 22:21 Uhr

  • Nur der Vollständigkeit halber, std::as_const wurde in C++17 eingeführt und ist äquivalent zu qAsConst.

    – cbuchart

    13. Februar 2020 um 11:28 Uhr

Qt hat eine Implementierung, um dies zu lösen, qAsConst (siehe https://doc.qt.io/qt-5/qtglobal.html#qAsConst). Die Dokumentation besagt, dass es sich um die Qt-Version von std::as_const() von C++17 handelt.

1012240cookie-checkKorrekte Verwendung der bereichsbasierten for-Schleife von C++11 in Qt

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

Privacy policy