Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Does Range-Based For on Qt Container Do a Deep Copy?
QtWS25 Last Chance

Does Range-Based For on Qt Container Do a Deep Copy?

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 4 Posters 895 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    Crag_Hack
    wrote on last edited by
    #1

    Hi I scoured the web for this information but couldn't verify my suspicions... I am wondering if you do a simple ranged based for loop on a non-shared Qt container if the loop does a deep copy of the container. So like this:

    QVector<QString> awesomeVector;
    awesomeVector << "awesome string" << "awesome string 2";
    for (QString& string : awesomeVector) // does this do a deep copy?
        string = "unawesome string";
    

    I'm betting my left arm there is no deep copy... hope I'm right or I'll be living the rest of my life without an arm. Thanks!

    Pl45m4P Chris KawaC 2 Replies Last reply
    0
    • C Offline
      C Offline
      Crag_Hack
      wrote on last edited by
      #11

      TLDR for googlers thx to Chris Kawa & JHilk:

      QVector<QString> awesomeVector;
      for (QString& string : awesomeVector) // deep copy if container is shared & reference count greater than 1, otherwise no deep copy
      
      
      const QVector<QString> awesomeVector;
      for (const QString& string : awesomeVector) // no deep copy
      
      
      QVector<QString> awesomeVector;
      for (const QString& string : qAsConst(awesomeVector)) // no deep copy
      
      1 Reply Last reply
      0
      • C Crag_Hack

        Hi I scoured the web for this information but couldn't verify my suspicions... I am wondering if you do a simple ranged based for loop on a non-shared Qt container if the loop does a deep copy of the container. So like this:

        QVector<QString> awesomeVector;
        awesomeVector << "awesome string" << "awesome string 2";
        for (QString& string : awesomeVector) // does this do a deep copy?
            string = "unawesome string";
        

        I'm betting my left arm there is no deep copy... hope I'm right or I'll be living the rest of my life without an arm. Thanks!

        Pl45m4P Offline
        Pl45m4P Offline
        Pl45m4
        wrote on last edited by
        #2

        @Crag_Hack

        You can read it yourself here

        • https://doc.qt.io/qt-5/qvector.html

        It's stated:

        Note that using non-const operators and functions can cause QVector to do a deep copy of the data. This is due to implicit sharing.

        QVector is, like many other Qt classes, implicity shared

        • https://doc.qt.io/qt-5/implicit-sharing.html#deep-copy

        If debugging is the process of removing software bugs, then programming must be the process of putting them in.

        ~E. W. Dijkstra

        1 Reply Last reply
        3
        • C Crag_Hack

          Hi I scoured the web for this information but couldn't verify my suspicions... I am wondering if you do a simple ranged based for loop on a non-shared Qt container if the loop does a deep copy of the container. So like this:

          QVector<QString> awesomeVector;
          awesomeVector << "awesome string" << "awesome string 2";
          for (QString& string : awesomeVector) // does this do a deep copy?
              string = "unawesome string";
          

          I'm betting my left arm there is no deep copy... hope I'm right or I'll be living the rest of my life without an arm. Thanks!

          Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by
          #3

          @Crag_Hack said:

          I'm betting my left arm there is no deep copy

          The appropriate services will be collecting your left arm shortly :)

          QVector<QString> awesomeVector;
          for (QString& string : awesomeVector) // copy
          
          
          const QVector<QString> awesomeVector;
          for (const QString& string : awesomeVector) // no copy
          
          
          QVector<QString> awesomeVector;
          for (const QString& string : qAsConst(awesomeVector)) // no copy
          
          1 Reply Last reply
          5
          • J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by
            #4

            I'm actually on the OP's side of this, Q_FOREACH does the deep copy no matter what, the standard c++ for range loop is "usually" fine when working for references

            as a proof:

            class MyClass {
            public:
                QString str;
            
                // Default constructor
                MyClass(QString s) : str(std::move(s)) {
                    qDebug() << "Default constructor called";
                }
            
                // Copy constructor
                MyClass(const MyClass& other) : str(other.str) {
                    qDebug() << "Copy constructor called";
                }
            
                // Move constructor
                MyClass(MyClass&& other) noexcept : str(std::move(other.str)) {
                    qDebug() << "Move constructor called";
                }
            
                // Copy assignment operator
                MyClass& operator=(const MyClass& other) {
                    str = other.str;
                    qDebug() << "Copy assignment operator called";
                    return *this;
                }
            
                // Move assignment operator
                MyClass& operator=(MyClass&& other) noexcept {
                    str = std::move(other.str);
                    qDebug() << "Move assignment operator called";
                    return *this;
                }
            };
            
            #include <QApplication>
            
            int main(int argc, char *argv[])
            {
            
                QApplication app(argc,argv);
            
            QVector<MyClass> awesomeVector;
                    awesomeVector << MyClass("awesome string") << MyClass("awesome string 2");
                    qDebug() << "Loop Start";
                    for (auto& myClass : awesomeVector) // does this do a deep copy?
                        myClass.str = "unawesome string";
                    qDebug() << (awesomeVector.at(0).str) << awesomeVector.at(1).str;
            
                return app.exec();
            }
            

            results in no copy on write.

            Default constructor called
            Move constructor called
            Default constructor called
            Move constructor called
            Loop Start
            "unawesome string" "unawesome string"
            

            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            Chris KawaC 1 Reply Last reply
            2
            • J.HilkJ J.Hilk

              I'm actually on the OP's side of this, Q_FOREACH does the deep copy no matter what, the standard c++ for range loop is "usually" fine when working for references

              as a proof:

              class MyClass {
              public:
                  QString str;
              
                  // Default constructor
                  MyClass(QString s) : str(std::move(s)) {
                      qDebug() << "Default constructor called";
                  }
              
                  // Copy constructor
                  MyClass(const MyClass& other) : str(other.str) {
                      qDebug() << "Copy constructor called";
                  }
              
                  // Move constructor
                  MyClass(MyClass&& other) noexcept : str(std::move(other.str)) {
                      qDebug() << "Move constructor called";
                  }
              
                  // Copy assignment operator
                  MyClass& operator=(const MyClass& other) {
                      str = other.str;
                      qDebug() << "Copy assignment operator called";
                      return *this;
                  }
              
                  // Move assignment operator
                  MyClass& operator=(MyClass&& other) noexcept {
                      str = std::move(other.str);
                      qDebug() << "Move assignment operator called";
                      return *this;
                  }
              };
              
              #include <QApplication>
              
              int main(int argc, char *argv[])
              {
              
                  QApplication app(argc,argv);
              
              QVector<MyClass> awesomeVector;
                      awesomeVector << MyClass("awesome string") << MyClass("awesome string 2");
                      qDebug() << "Loop Start";
                      for (auto& myClass : awesomeVector) // does this do a deep copy?
                          myClass.str = "unawesome string";
                      qDebug() << (awesomeVector.at(0).str) << awesomeVector.at(1).str;
              
                  return app.exec();
              }
              

              results in no copy on write.

              Default constructor called
              Move constructor called
              Default constructor called
              Move constructor called
              Loop Start
              "unawesome string" "unawesome string"
              
              Chris KawaC Offline
              Chris KawaC Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on last edited by Chris Kawa
              #5

              @J-Hilk Granted, this is a bit of a synthetic example, and since the vector is not actually shared at that point there is no copy.

              But the copy happens if vector is shared. It happens in the non-const begin() member of the vector. If you run it under a debugger and break in the detach() method you'll see a callstack like this:

              QList<MyClass>::detach
              QList<MyCLass>::begin  // <- this is called by the for loop
              main
              ...
              

              The detach does copy if vector is shared at that point, so yeah, to be strict:

              QVector<QString> awesomeVector;
              for (QString& string : awesomeVector) // this won't copy
              
              
              QVector<QString> awesomeVector;
              QVector<QString> sharedVector = awesomeVector; // this won't copy
              for (QString& string : awesomeVector) // but this will
              

              so it won't copy, but it will detach, and detach might copy.

              J.HilkJ 1 Reply Last reply
              4
              • Chris KawaC Chris Kawa

                @J-Hilk Granted, this is a bit of a synthetic example, and since the vector is not actually shared at that point there is no copy.

                But the copy happens if vector is shared. It happens in the non-const begin() member of the vector. If you run it under a debugger and break in the detach() method you'll see a callstack like this:

                QList<MyClass>::detach
                QList<MyCLass>::begin  // <- this is called by the for loop
                main
                ...
                

                The detach does copy if vector is shared at that point, so yeah, to be strict:

                QVector<QString> awesomeVector;
                for (QString& string : awesomeVector) // this won't copy
                
                
                QVector<QString> awesomeVector;
                QVector<QString> sharedVector = awesomeVector; // this won't copy
                for (QString& string : awesomeVector) // but this will
                

                so it won't copy, but it will detach, and detach might copy.

                J.HilkJ Offline
                J.HilkJ Offline
                J.Hilk
                Moderators
                wrote on last edited by
                #6

                @Chris-Kawa 👍 very true

                But I was technically correct, the best kind of correct 🤓

                alt text


                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                Chris KawaC 1 Reply Last reply
                2
                • J.HilkJ J.Hilk

                  @Chris-Kawa 👍 very true

                  But I was technically correct, the best kind of correct 🤓

                  alt text

                  Chris KawaC Offline
                  Chris KawaC Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on last edited by Chris Kawa
                  #7

                  @J-Hilk yeah yeah, OP can keep the arm I guess. We'll get it next time :P

                  EDIT: an example came to mind:

                  void func(QVector<QString> awesomeVector) // this does shallow copy/sharing
                  {
                     for (QString& string : awesomeVector) // so this detach will always copy
                  

                  so it's all about the context in which you run the for loop.

                  1 Reply Last reply
                  5
                  • C Offline
                    C Offline
                    Crag_Hack
                    wrote on last edited by
                    #8

                    Excellent thanks guys. Exactly the information I was looking for.

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      Crag_Hack
                      wrote on last edited by Crag_Hack
                      #9

                      I think this all makes perfect sense since according to here it looks like the ranged based for loop uses iterators behind the scenes. And the non-const iterators won't trigger detach unless the container is shared. Which leads me to another question... will the following detach for a non-shared vector? Won't it just copy the string in the loop to the variable string each iteration but not actually do a deep copy of the container? Whereas if you do for (QString& string : awesomeVector) it just copies the variable string by reference.

                      QVector<QString> awesomeVector; //non-shared Qt container
                      for (QString string : awesomeVector)
                      
                      Chris KawaC 1 Reply Last reply
                      0
                      • C Crag_Hack

                        I think this all makes perfect sense since according to here it looks like the ranged based for loop uses iterators behind the scenes. And the non-const iterators won't trigger detach unless the container is shared. Which leads me to another question... will the following detach for a non-shared vector? Won't it just copy the string in the loop to the variable string each iteration but not actually do a deep copy of the container? Whereas if you do for (QString& string : awesomeVector) it just copies the variable string by reference.

                        QVector<QString> awesomeVector; //non-shared Qt container
                        for (QString string : awesomeVector)
                        
                        Chris KawaC Offline
                        Chris KawaC Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on last edited by
                        #10

                        @Crag_Hack It's the same for reference and value cases. A range for loop is roughly equivalent to this:

                        for (auto it = awesomeVector.begin(); it != awesomeVector.end(); ++it)
                        {
                           QString string = *it; // in case of by value loop
                           QString& string = *it; // in case of by reference loop
                           ...
                        }
                        

                        The detach of the vector happens already in the call to begin(), so whether the value is later referenced or copied doesn't change the outcome. The only factor here is whether the vector is shared at that point or not.

                        That's why you should use qAsConst (or the std::as_const equivalent) when you only want to read from the vector. It basically const casts the vector, co calling awesomeVector.begin() uses const overload of begin, which doesn't detach.

                        1 Reply Last reply
                        3
                        • C Offline
                          C Offline
                          Crag_Hack
                          wrote on last edited by
                          #11

                          TLDR for googlers thx to Chris Kawa & JHilk:

                          QVector<QString> awesomeVector;
                          for (QString& string : awesomeVector) // deep copy if container is shared & reference count greater than 1, otherwise no deep copy
                          
                          
                          const QVector<QString> awesomeVector;
                          for (const QString& string : awesomeVector) // no deep copy
                          
                          
                          QVector<QString> awesomeVector;
                          for (const QString& string : qAsConst(awesomeVector)) // no deep copy
                          
                          1 Reply Last reply
                          0
                          • C Crag_Hack has marked this topic as solved on

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved