Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. Containers with C++17 structured bindings
QtWS25 Last Chance

Containers with C++17 structured bindings

Scheduled Pinned Locked Moved Solved C++ Gurus
13 Posts 7 Posters 3.1k 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.
  • D Offline
    D Offline
    deleted541
    wrote on 9 May 2019, 11:24 last edited by deleted541 5 Sept 2019, 11:24
    #1

    Since C++ 17, structured bindings exist in the language. These are very useful for iterating over maps for example.
    This works fine for std::map, but unfortunately does not work for Qt Containers like QMap.
    Is there a plan to add this in and/or a way to make it work in a similar way?
    Or should I rather use plain iterators instead?

    Example code:

    #include <QString>
    #include <QMap>
    #include <map>
    #include <QDebug>
    
    int main()
    {
    	const QMap<int, QString> qt_m =
    	{
    		{21, "hello"},
    		{684, "second"},
    		{831, "end"}
    	};
    	const auto std_m = qt_m.toStdMap();
    
    	//structured bindings to iterate over std::map. works fine
    	for( const auto& [key, value] : std_m )
    	{
    		qDebug() << key << " : " << value << '\n';
    	}
    
    	//structured bindings to iterate over QMap. does not compile
    	for( const auto& [key, value] : qt_m )
    	{
    		qDebug() << key << " : " << value << '\n';
    	}
     }
    
    
    J 1 Reply Last reply 9 May 2019, 12:12
    0
    • D deleted541
      9 May 2019, 11:24

      Since C++ 17, structured bindings exist in the language. These are very useful for iterating over maps for example.
      This works fine for std::map, but unfortunately does not work for Qt Containers like QMap.
      Is there a plan to add this in and/or a way to make it work in a similar way?
      Or should I rather use plain iterators instead?

      Example code:

      #include <QString>
      #include <QMap>
      #include <map>
      #include <QDebug>
      
      int main()
      {
      	const QMap<int, QString> qt_m =
      	{
      		{21, "hello"},
      		{684, "second"},
      		{831, "end"}
      	};
      	const auto std_m = qt_m.toStdMap();
      
      	//structured bindings to iterate over std::map. works fine
      	for( const auto& [key, value] : std_m )
      	{
      		qDebug() << key << " : " << value << '\n';
      	}
      
      	//structured bindings to iterate over QMap. does not compile
      	for( const auto& [key, value] : qt_m )
      	{
      		qDebug() << key << " : " << value << '\n';
      	}
       }
      
      
      J Offline
      J Offline
      jsulm
      Lifetime Qt Champion
      wrote on 9 May 2019, 12:12 last edited by jsulm 5 Sept 2019, 12:12
      #2

      @david_dlqa said in Containers with C++17 structured bindings:

      Is there a plan to add this in and/or a way to make it work in a similar way?

      Hi and welcome!

      This is more a question for Qt developers (on their mailing list). Here we are in a user forum.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • D Offline
        D Offline
        deleted541
        wrote on 9 May 2019, 12:27 last edited by
        #3

        Hi, thanks for letting me know.
        I guess the question then is, if plain iterators are the best way to do this if I need both the key and value in a loop.

        1 Reply Last reply
        0
        • C Online
          C Online
          Chris Kawa
          Lifetime Qt Champion
          wrote on 9 May 2019, 12:47 last edited by
          #4

          Unlike std::map Qt does not store the key/value in a pair, so you can't bind your structure to the result of operator*. It returns only the value.

          If you need both key and a value you can either use the map's iterator:

          for( auto it = qt_m.cbegin(); it != qt_m.cend(); ++it )
          {
              qDebug() << it.key() << " : " << it.value() << '\n';
          }
          

          or the iterator class:

          QMapIterator it(qt_m);
          while(it.hasNext())
          {
              it.next();
              qDebug() << it.key() << " : " << it.value() << '\n';
          }
          
          1 Reply Last reply
          7
          • F Offline
            F Offline
            fcarney
            wrote on 9 May 2019, 15:11 last edited by
            #5

            Or:

            for(auto key: qt_m.keys()){
              qInfo() << key << qt_m[key];
            }
            

            C++ is a perfectly valid school of magic.

            C 1 Reply Last reply 9 May 2019, 15:18
            0
            • F fcarney
              9 May 2019, 15:11

              Or:

              for(auto key: qt_m.keys()){
                qInfo() << key << qt_m[key];
              }
              
              C Online
              C Online
              Chris Kawa
              Lifetime Qt Champion
              wrote on 9 May 2019, 15:18 last edited by
              #6

              @fcarney Yeah, but that's a very bad solution. First keys() makes a copy of the data into a new container and then you make a find for each element (that's what operator[] is essentially). It's terrible both performance wise and for memory consumption. Please don't do this in production code.

              1 Reply Last reply
              3
              • F Offline
                F Offline
                fcarney
                wrote on 9 May 2019, 15:44 last edited by
                #7

                @Chris-Kawa said in Containers with C++17 structured bindings:

                It's terrible both performance wise and for memory consumption.

                Now I can see why keys() is not in the std::map. It would encourage poor coding practices. Thanks for your insight. I generally use this to see whats in my map for debugging, but yeah I can see the performance problems it would create on large datasets.

                C++ is a perfectly valid school of magic.

                K 1 Reply Last reply 9 May 2019, 21:43
                0
                • S Offline
                  S Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 9 May 2019, 20:13 last edited by
                  #8

                  Hi,

                  Just in case, there are key/value iterators for both QMap and QHash:
                  QMap::keyValueBegin
                  QMap::keyValueEnd
                  QMap::constKeyValueBegin
                  QMap::constKeyValueEnd
                  QHash::keyValueBegin
                  QHash::keyValueEnd
                  QHash::constKeyValueBegin
                  QHash::constKeyValueEnd

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  4
                  • C Online
                    C Online
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 9 May 2019, 20:39 last edited by
                    #9

                    @SGaist Huh, that's cool. I didn't know those existed. Thanks!

                    @david_dlqa So you could write a little adapter thingy like this:

                    template<class Container> class adapt
                    {
                    public:
                        adapt(const Container& container) : c(container) {}
                        typename Container::const_key_value_iterator begin() const { return c.keyValueBegin(); }
                        typename Container::const_key_value_iterator end() const { return c.keyValueEnd(); }
                    private:
                        const Container& c;
                    };
                    

                    and then you could use structured bindings like so:

                    for( const auto& [key, value] : adapt(qt_m) )
                    {
                        qDebug() << key << " : " << value << '\n';
                    }
                    

                    From what i see it's still an inferior solution performance wise, as those keyValue iterators make pairs on the fly, but it might be good enough.

                    D 1 Reply Last reply 10 May 2019, 08:09
                    5
                    • F fcarney
                      9 May 2019, 15:44

                      @Chris-Kawa said in Containers with C++17 structured bindings:

                      It's terrible both performance wise and for memory consumption.

                      Now I can see why keys() is not in the std::map. It would encourage poor coding practices. Thanks for your insight. I generally use this to see whats in my map for debugging, but yeah I can see the performance problems it would create on large datasets.

                      K Offline
                      K Offline
                      kshegunov
                      Moderators
                      wrote on 9 May 2019, 21:43 last edited by
                      #10

                      @fcarney said in Containers with C++17 structured bindings:

                      I generally use this to see whats in my map for debugging, but yeah I can see the performance problems it would create on large datasets.

                      You don't want to keep large datasets in a map to begin with. [1]

                      [1]: https://woboq.com/blog/qmap_qhash_benchmark.html

                      Read and abide by the Qt Code of Conduct

                      1 Reply Last reply
                      1
                      • C Chris Kawa
                        9 May 2019, 20:39

                        @SGaist Huh, that's cool. I didn't know those existed. Thanks!

                        @david_dlqa So you could write a little adapter thingy like this:

                        template<class Container> class adapt
                        {
                        public:
                            adapt(const Container& container) : c(container) {}
                            typename Container::const_key_value_iterator begin() const { return c.keyValueBegin(); }
                            typename Container::const_key_value_iterator end() const { return c.keyValueEnd(); }
                        private:
                            const Container& c;
                        };
                        

                        and then you could use structured bindings like so:

                        for( const auto& [key, value] : adapt(qt_m) )
                        {
                            qDebug() << key << " : " << value << '\n';
                        }
                        

                        From what i see it's still an inferior solution performance wise, as those keyValue iterators make pairs on the fly, but it might be good enough.

                        D Offline
                        D Offline
                        deleted541
                        wrote on 10 May 2019, 08:09 last edited by
                        #11

                        @Chris-Kawa
                        Nice, thats very helpful. I think the performance penalty here is negligible. I ran a test on my machine which produced these results in release mode:

                        const iter 2799
                        non-const iter 2835
                        adapt const-reference 2819
                        adapt copy 2808
                        

                        Test code:

                        #include <QMap>
                        #include <map>
                        #include <QDebug>
                        #include <QElapsedTimer>
                        
                        template<class Container> class adapt
                        {
                        public:
                        	adapt(const Container& container) : c(container) {}
                        	typename Container::const_key_value_iterator begin() const { return c.keyValueBegin(); }
                        	typename Container::const_key_value_iterator end() const { return c.keyValueEnd(); }
                        private:
                        	const Container& c;
                        };
                        
                        int main()
                        {
                        	QMap<int, QString> qt_m;
                        	for(int i = 0; i < (1<<24); ++i) qt_m.insert(rand(), QString::number(rand()));
                        	QString a;
                        	int b;
                        
                        	QElapsedTimer t;
                        	t.start();
                        	for( auto it = qt_m.cbegin(); it != qt_m.cend(); ++it )
                        	{
                        		a = *it;
                        		b = it.key();
                        	}
                        	qDebug() << "const iter" << t.elapsed();
                        
                        	t.restart();
                        	for( auto it = qt_m.begin(); it != qt_m.end(); ++it )
                        	{
                        		a = *it;
                        		b = it.key();
                        	}
                        	qDebug() << "non-const iter" << t.elapsed();
                        
                        	t.restart();
                        	for( const auto& [key, value] : adapt(qt_m) )
                        	{
                        		a = value;
                        		b = key;
                        	}
                        	qDebug() << "adapt const-reference" << t.elapsed();
                        
                        	t.restart();
                        	for( auto [key, value] : adapt(qt_m) )
                        	{
                        		a = value;
                        		b = key;
                        	}
                        	qDebug() << "adapt copy" << t.elapsed();
                         }
                        
                        J.HilkJ 1 Reply Last reply 10 May 2019, 08:28
                        0
                        • D deleted541
                          10 May 2019, 08:09

                          @Chris-Kawa
                          Nice, thats very helpful. I think the performance penalty here is negligible. I ran a test on my machine which produced these results in release mode:

                          const iter 2799
                          non-const iter 2835
                          adapt const-reference 2819
                          adapt copy 2808
                          

                          Test code:

                          #include <QMap>
                          #include <map>
                          #include <QDebug>
                          #include <QElapsedTimer>
                          
                          template<class Container> class adapt
                          {
                          public:
                          	adapt(const Container& container) : c(container) {}
                          	typename Container::const_key_value_iterator begin() const { return c.keyValueBegin(); }
                          	typename Container::const_key_value_iterator end() const { return c.keyValueEnd(); }
                          private:
                          	const Container& c;
                          };
                          
                          int main()
                          {
                          	QMap<int, QString> qt_m;
                          	for(int i = 0; i < (1<<24); ++i) qt_m.insert(rand(), QString::number(rand()));
                          	QString a;
                          	int b;
                          
                          	QElapsedTimer t;
                          	t.start();
                          	for( auto it = qt_m.cbegin(); it != qt_m.cend(); ++it )
                          	{
                          		a = *it;
                          		b = it.key();
                          	}
                          	qDebug() << "const iter" << t.elapsed();
                          
                          	t.restart();
                          	for( auto it = qt_m.begin(); it != qt_m.end(); ++it )
                          	{
                          		a = *it;
                          		b = it.key();
                          	}
                          	qDebug() << "non-const iter" << t.elapsed();
                          
                          	t.restart();
                          	for( const auto& [key, value] : adapt(qt_m) )
                          	{
                          		a = value;
                          		b = key;
                          	}
                          	qDebug() << "adapt const-reference" << t.elapsed();
                          
                          	t.restart();
                          	for( auto [key, value] : adapt(qt_m) )
                          	{
                          		a = value;
                          		b = key;
                          	}
                          	qDebug() << "adapt copy" << t.elapsed();
                           }
                          
                          J.HilkJ Offline
                          J.HilkJ Offline
                          J.Hilk
                          Moderators
                          wrote on 10 May 2019, 08:28 last edited by
                          #12

                          @david_dlqa said
                          Just a word of caution, as I ran into this as well.

                          If you do speed tests with QElapsedTimer, do not simply print the elapsed() value to qDebug().

                          qDebug() << "const iter" << t.elapsed();

                          the qDebug() call will take a significant amount of time.

                          I usually store the elapsed value in a local variable and print that one at time insensitive points (the end)


                          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.

                          D 1 Reply Last reply 10 May 2019, 09:01
                          0
                          • J.HilkJ J.Hilk
                            10 May 2019, 08:28

                            @david_dlqa said
                            Just a word of caution, as I ran into this as well.

                            If you do speed tests with QElapsedTimer, do not simply print the elapsed() value to qDebug().

                            qDebug() << "const iter" << t.elapsed();

                            the qDebug() call will take a significant amount of time.

                            I usually store the elapsed value in a local variable and print that one at time insensitive points (the end)

                            D Offline
                            D Offline
                            deleted541
                            wrote on 10 May 2019, 09:01 last edited by
                            #13

                            @J.Hilk
                            Thanks, that makes sense. I just copied that from the documentation hence I thought that was not a concern.
                            I reran the tests and got almost the same results, so I think with times in the seconds, the qDebug overhead is not significant

                            1 Reply Last reply
                            0

                            2/13

                            9 May 2019, 12:12

                            topic:navigator.unread, 11
                            • Login

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