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. How to avoid mixing const_iterator and iterator when using QList::erase?
Forum Updated to NodeBB v4.3 + New Features

How to avoid mixing const_iterator and iterator when using QList::erase?

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 5 Posters 2.6k Views 2 Watching
  • 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.
  • J Offline
    J Offline
    Jack Hill
    wrote on 5 Feb 2024, 15:47 last edited by Jack Hill 2 May 2024, 15:52
    #1

    Hi,
    suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

    void MyListModel::removeValues(int lowerBound, int upperBound)
    {
        int row = 0;
        auto itr = m_numbers.begin();
        while (itr != m_numbers.end()) {
            if (lowerBound <= *itr && *itr <= upperBound) {
                beginRemoveRows(parent, row, row);
                itr = m_numbers.erase(itr);
                endRemoveRows();
            } else {
                itr++;
                row++;
            }
        }
    }
    

    This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

    1. is this clazy warning something to worry about?
    2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
    M J J K 4 Replies Last reply 5 Feb 2024, 16:28
    0
    • J Jack Hill
      5 Feb 2024, 15:47

      Hi,
      suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

      void MyListModel::removeValues(int lowerBound, int upperBound)
      {
          int row = 0;
          auto itr = m_numbers.begin();
          while (itr != m_numbers.end()) {
              if (lowerBound <= *itr && *itr <= upperBound) {
                  beginRemoveRows(parent, row, row);
                  itr = m_numbers.erase(itr);
                  endRemoveRows();
              } else {
                  itr++;
                  row++;
              }
          }
      }
      

      This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

      1. is this clazy warning something to worry about?
      2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
      K Offline
      K Offline
      kshegunov
      Moderators
      wrote on 5 Feb 2024, 23:34 last edited by kshegunov 2 Jun 2024, 12:21
      #13

      @Jack-Hill said in How to avoid mixing const_iterator and iterator when using QList::erase?:

      This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos)

      Then don't. This can be as simple as:

      for (int i = 0, size = m_numbers.size(); i < size; i++) {
          auto value = m_numbers[i];
          if (value < lowerBound || value > upperBound)
              continue;
          beginRemoveRows(parent, i, i); // for whatever `parent` is here, it isn't clear where it comes from
          m_numbers.removeAt(i--);
          size--;
          endRemoveRows();
      }
      

      If you want to minimize the number of draws the view does on the other hand you'd do two passes - take the bounds of elements to be removed and only then remove them in batches.

      [Edit: Fixed wrong boolean operation ~kshegunov]

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • J Jack Hill
        5 Feb 2024, 15:47

        Hi,
        suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

        void MyListModel::removeValues(int lowerBound, int upperBound)
        {
            int row = 0;
            auto itr = m_numbers.begin();
            while (itr != m_numbers.end()) {
                if (lowerBound <= *itr && *itr <= upperBound) {
                    beginRemoveRows(parent, row, row);
                    itr = m_numbers.erase(itr);
                    endRemoveRows();
                } else {
                    itr++;
                    row++;
                }
            }
        }
        

        This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

        1. is this clazy warning something to worry about?
        2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
        M Offline
        M Offline
        mpergand
        wrote on 5 Feb 2024, 16:28 last edited by
        #2

        @Jack-Hill said in How to avoid mixing const_iterator and iterator when using QList::erase?:

        I have clazy complaining about mixing iterators

        Try using cbegin() and cend()

        I faced similar issues some time ago:

         /*
                  1) Use cbegin and cend to be explicit about using const iterator.
                  2) begin() and end() return const_iterator when method is declared as const
              */
        
                        typedef typename std::vector<E*>::const_iterator const_node_it;
                         const_node_it  begin() const
                                    {
                                    return _list.cbegin();
                                    }
        
                        const_node_it  end() const
                                    {
                                    return _list.cend();
        
                                        }
        
        
        J 1 Reply Last reply 5 Feb 2024, 17:37
        0
        • J Jack Hill
          5 Feb 2024, 15:47

          Hi,
          suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

          void MyListModel::removeValues(int lowerBound, int upperBound)
          {
              int row = 0;
              auto itr = m_numbers.begin();
              while (itr != m_numbers.end()) {
                  if (lowerBound <= *itr && *itr <= upperBound) {
                      beginRemoveRows(parent, row, row);
                      itr = m_numbers.erase(itr);
                      endRemoveRows();
                  } else {
                      itr++;
                      row++;
                  }
              }
          }
          

          This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

          1. is this clazy warning something to worry about?
          2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
          J Offline
          J Offline
          JoeCFD
          wrote on 5 Feb 2024, 16:46 last edited by
          #3

          @Jack-Hill your code is not efficient and may be dangerous. Better to loop in reverse order when items are deleted.

          1 Reply Last reply
          0
          • J Jack Hill
            5 Feb 2024, 15:47

            Hi,
            suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

            void MyListModel::removeValues(int lowerBound, int upperBound)
            {
                int row = 0;
                auto itr = m_numbers.begin();
                while (itr != m_numbers.end()) {
                    if (lowerBound <= *itr && *itr <= upperBound) {
                        beginRemoveRows(parent, row, row);
                        itr = m_numbers.erase(itr);
                        endRemoveRows();
                    } else {
                        itr++;
                        row++;
                    }
                }
            }
            

            This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

            1. is this clazy warning something to worry about?
            2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
            J Offline
            J Offline
            JonB
            wrote on 5 Feb 2024, 17:33 last edited by JonB 2 May 2024, 17:34
            #4

            @Jack-Hill
            Just an observation about algorithm, not your issue. You call begin/endRemoveRows() once to remove one row at a time. I would prefer to call to once only with the range to be removed. That may help efficiency if things act on it.

            J 1 Reply Last reply 5 Feb 2024, 17:51
            1
            • M mpergand
              5 Feb 2024, 16:28

              @Jack-Hill said in How to avoid mixing const_iterator and iterator when using QList::erase?:

              I have clazy complaining about mixing iterators

              Try using cbegin() and cend()

              I faced similar issues some time ago:

               /*
                        1) Use cbegin and cend to be explicit about using const iterator.
                        2) begin() and end() return const_iterator when method is declared as const
                    */
              
                              typedef typename std::vector<E*>::const_iterator const_node_it;
                               const_node_it  begin() const
                                          {
                                          return _list.cbegin();
                                          }
              
                              const_node_it  end() const
                                          {
                                          return _list.cend();
              
                                              }
              
              
              J Offline
              J Offline
              Jack Hill
              wrote on 5 Feb 2024, 17:37 last edited by
              #5

              @mpergand I don't think that would work because erase returns a non-const iterator

              @JoeCFD You mean something like this?

              int row = m_numbers.size() - 1;
              auto itr = m_numbers.end();
              while (itr != m_numbers.begin()) {
                  if (lowerBound <= *itr && *itr <= upperBound) {
                      beginRemoveRows(parent, row, row);
                      itr = m_numbers.erase(itr);
                      endRemoveRows();
                  } else {
                      itr--;
                      row--;
                  }
              }
              
              J 1 Reply Last reply 5 Feb 2024, 17:59
              0
              • J JonB
                5 Feb 2024, 17:33

                @Jack-Hill
                Just an observation about algorithm, not your issue. You call begin/endRemoveRows() once to remove one row at a time. I would prefer to call to once only with the range to be removed. That may help efficiency if things act on it.

                J Offline
                J Offline
                Jack Hill
                wrote on 5 Feb 2024, 17:51 last edited by
                #6

                @JonB True. Would probably be better to remove consecutive items in one go instead of one-by-one.

                Also I just realised I think it would be better not bother with iterators and just use indices directly like so (not tested)

                for (int row = 0; row < m_numbers.size(); ) {
                    const int num = m_numbers[row];
                    if (lowerBound <= num && num <= upperBound) {
                        beginRemoveRows(parent, row, row);
                        m_numbers.remove(row);
                        endRemoveRows();
                    } else {
                        row++;
                    }
                }
                
                J 1 Reply Last reply 5 Feb 2024, 17:54
                0
                • J Jack Hill
                  5 Feb 2024, 17:51

                  @JonB True. Would probably be better to remove consecutive items in one go instead of one-by-one.

                  Also I just realised I think it would be better not bother with iterators and just use indices directly like so (not tested)

                  for (int row = 0; row < m_numbers.size(); ) {
                      const int num = m_numbers[row];
                      if (lowerBound <= num && num <= upperBound) {
                          beginRemoveRows(parent, row, row);
                          m_numbers.remove(row);
                          endRemoveRows();
                      } else {
                          row++;
                      }
                  }
                  
                  J Offline
                  J Offline
                  JonB
                  wrote on 5 Feb 2024, 17:54 last edited by
                  #7

                  @Jack-Hill said in How to avoid mixing const_iterator and iterator when using QList::erase?:

                  Also I just realised I think it would be better not bother with iterators and just use indices directly like so (not tested)

                  :) Don't know what to say. Then this topic wouldn't exist. All these iterators and consts. I was a C guy, it was just so much easier! I have to look up iterators each time, and don't use them if I feel I don't have to/can't be bothered. But I'm sure the C++ experts around these days will recommend you do.

                  1 Reply Last reply
                  0
                  • J Jack Hill
                    5 Feb 2024, 17:37

                    @mpergand I don't think that would work because erase returns a non-const iterator

                    @JoeCFD You mean something like this?

                    int row = m_numbers.size() - 1;
                    auto itr = m_numbers.end();
                    while (itr != m_numbers.begin()) {
                        if (lowerBound <= *itr && *itr <= upperBound) {
                            beginRemoveRows(parent, row, row);
                            itr = m_numbers.erase(itr);
                            endRemoveRows();
                        } else {
                            itr--;
                            row--;
                        }
                    }
                    
                    J Offline
                    J Offline
                    JoeCFD
                    wrote on 5 Feb 2024, 17:59 last edited by
                    #8

                    @Jack-Hill index is ok. But your iterator does not match it.
                    https://doc.qt.io/qt-5/qlist.html#crbegin

                    J 1 Reply Last reply 5 Feb 2024, 18:42
                    0
                    • J JoeCFD
                      5 Feb 2024, 17:59

                      @Jack-Hill index is ok. But your iterator does not match it.
                      https://doc.qt.io/qt-5/qlist.html#crbegin

                      J Offline
                      J Offline
                      Jack Hill
                      wrote on 5 Feb 2024, 18:42 last edited by Jack Hill 2 May 2024, 18:46
                      #9

                      @JoeCFD I don't think it's possible to use reverse iterators because there's no matching erase function.

                      @JonB Indeed. This is from an existing code-base, so I didn't immediately think of switching to indices.

                      J 1 Reply Last reply 5 Feb 2024, 19:10
                      0
                      • J Jack Hill
                        5 Feb 2024, 18:42

                        @JoeCFD I don't think it's possible to use reverse iterators because there's no matching erase function.

                        @JonB Indeed. This is from an existing code-base, so I didn't immediately think of switching to indices.

                        J Offline
                        J Offline
                        JoeCFD
                        wrote on 5 Feb 2024, 19:10 last edited by
                        #10

                        @Jack-Hill

                        #include <QList>
                        
                        #include <iostream>
                        
                        int main()
                        {
                            QList< int > list{ 1,2,3,4,5,6,7,8,9,10 };
                        
                            auto iter = list.rbegin();
                            while( iter != list.rend() ) {
                                if ( 0 == *iter % 3 ) {
                                    list.erase( std::next( iter ).base() );
                                }
                                
                                iter++;
                            }
                            
                            for ( auto const value : list ) {
                                std::cout << value << std::endl;
                            }
                            
                            return 0;
                        }
                        
                        
                        J 1 Reply Last reply 5 Feb 2024, 20:51
                        0
                        • J JoeCFD
                          5 Feb 2024, 19:10

                          @Jack-Hill

                          #include <QList>
                          
                          #include <iostream>
                          
                          int main()
                          {
                              QList< int > list{ 1,2,3,4,5,6,7,8,9,10 };
                          
                              auto iter = list.rbegin();
                              while( iter != list.rend() ) {
                                  if ( 0 == *iter % 3 ) {
                                      list.erase( std::next( iter ).base() );
                                  }
                                  
                                  iter++;
                              }
                              
                              for ( auto const value : list ) {
                                  std::cout << value << std::endl;
                              }
                              
                              return 0;
                          }
                          
                          
                          J Offline
                          J Offline
                          Jack Hill
                          wrote on 5 Feb 2024, 20:51 last edited by
                          #11

                          @JoeCFD That does work but it still triggers the clazy warning. It seems to me like the safest way of imperatively erasing items from QLists is to iterate with indices.

                          J 1 Reply Last reply 5 Feb 2024, 23:15
                          0
                          • J Jack Hill
                            5 Feb 2024, 20:51

                            @JoeCFD That does work but it still triggers the clazy warning. It seems to me like the safest way of imperatively erasing items from QLists is to iterate with indices.

                            J Offline
                            J Offline
                            JoeCFD
                            wrote on 5 Feb 2024, 23:15 last edited by JoeCFD 2 May 2024, 23:16
                            #12

                            @Jack-Hill I did not see any warning with g++ 11.3.0 on Ubuntu 22.04.

                            1 Reply Last reply
                            0
                            • J Jack Hill
                              5 Feb 2024, 15:47

                              Hi,
                              suppose we have a list model that has a backend m_numbers = QList<int> and we implement a function to remove rows that satisfy the predicate lowerBound <= value <= upperBound

                              void MyListModel::removeValues(int lowerBound, int upperBound)
                              {
                                  int row = 0;
                                  auto itr = m_numbers.begin();
                                  while (itr != m_numbers.end()) {
                                      if (lowerBound <= *itr && *itr <= upperBound) {
                                          beginRemoveRows(parent, row, row);
                                          itr = m_numbers.erase(itr);
                                          endRemoveRows();
                                      } else {
                                          itr++;
                                          row++;
                                      }
                                  }
                              }
                              

                              This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos). So,

                              1. is this clazy warning something to worry about?
                              2. is there a way of rewriting this function to quell the clazy warning? It seems odd to me that there is no QList::const_iterator QList::erase(QList::const_iterator pos) or QList::iterator QList::erase(QList::iterator pos)
                              K Offline
                              K Offline
                              kshegunov
                              Moderators
                              wrote on 5 Feb 2024, 23:34 last edited by kshegunov 2 Jun 2024, 12:21
                              #13

                              @Jack-Hill said in How to avoid mixing const_iterator and iterator when using QList::erase?:

                              This works fine but I have clazy complaining about mixing iterators with const_iterators due to QList::iterator QList::erase(QList::const_iterator pos)

                              Then don't. This can be as simple as:

                              for (int i = 0, size = m_numbers.size(); i < size; i++) {
                                  auto value = m_numbers[i];
                                  if (value < lowerBound || value > upperBound)
                                      continue;
                                  beginRemoveRows(parent, i, i); // for whatever `parent` is here, it isn't clear where it comes from
                                  m_numbers.removeAt(i--);
                                  size--;
                                  endRemoveRows();
                              }
                              

                              If you want to minimize the number of draws the view does on the other hand you'd do two passes - take the bounds of elements to be removed and only then remove them in batches.

                              [Edit: Fixed wrong boolean operation ~kshegunov]

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply
                              1
                              • J Jack Hill has marked this topic as solved on 13 Mar 2024, 14:59

                              1/13

                              5 Feb 2024, 15:47

                              • Login

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