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

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.
  • C Offline
    C Offline
    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 Offline
        C Offline
        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 Offline
              C Offline
              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 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 Offline
                    J 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 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

                      13/13

                      10 May 2019, 09:01

                      • Login

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