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. Return pointer-to-member in const method
QtWS25 Last Chance

Return pointer-to-member in const method

Scheduled Pinned Locked Moved Solved C++ Gurus
35 Posts 6 Posters 8.4k 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 Chris Kawa
    29 Oct 2020, 18:12

    @JonB said in Return pointer-to-member in const method:

    The problem here is, that function returning int does not exhibit the problem!

    Exactly, it's intentional on my part and that's the point. Don't try to solve an ugly problem. Redesign and untangle dependencies so there is no problem in the first place ;) int* is an "index" and int* points to data. Untangle those roles.

    Anyways. My head is hurting on this.

    I fear I might have accidentally terrorized you into being paranoid about something that will bring you marginal gains and make your code a lot worse to read/maintain. I'd say, just for test, do the duplicated const and non-const methods, measure how much gain are you actually getting, decide if it's worth it and only then proceed or revert. While I care deeply about performance there is a line below which it's just not worth it, as in how much optimizations can you achieve in a finite amount of time and how it reflects on readability and ease of maintenance. I'd just like that line to be lower than it usually is, but it's up to you really. Don't let me pressure you too much. I've been told I can be bossy ;)

    J Offline
    J Offline
    JonB
    wrote on 29 Oct 2020, 18:32 last edited by JonB
    #19

    @Chris-Kawa said in Return pointer-to-member in const method:

    measure how much gain are you actually getting, decide if it's worth it and only then proceed or revert

    Sounds like what I would say :)

    While I care deeply about performance

    I do, but kinda more just in an algorithmic sense than whether it makes any visible difference to what I'm doing.

    Don't let me pressure you too much. I've been told I can be bossy ;)

    Not at all! I read your posts with interest, high quality.

    This has all revealed something to me which I had not appreciated. I thought Class::method() const only guaranteed that it did not alter *this. I did not expect that, for safety, it also does not allow Class::Member *Class::method() const. That function does not itself alter *this, but I do see that it returns a write-pointer into const this * which could later be used to do so. Hence you have to make that return a const * if you want method() const I just was not aware of this.

    I'm sure there are just pages of C++ specs I could/ought to read up on const.... [Actually I think I did so a while ago, I recall it being longggggg.]

    P.S.
    When I started C it didn't have const yet. Lambs gambolled carefree in the fields, life was easy then...

    1 Reply Last reply
    2
    • C Offline
      C Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on 29 Oct 2020, 19:18 last edited by
      #20

      If you want some further discussion points, take a look at how the Qt api returns pointers:

      QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
      QUndoStack *QUndoGroup::activeStack() const
      QObject *QDropEvent::source() const

      want more? :)

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      2
      • C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 29 Oct 2020, 19:29 last edited by
        #21

        const is kinda like the GPL license - infectious and intentionally so :)
        I won't bring the actual standardese page number, but a close-enough rule for this is on cppreference:

        If the operand is an lvalue expression of some object or function type T, operator& creates and returns a prvalue of type T*, with the same cv qualification, that is pointing to the object or function designated by the operand.

        What this piece of the typical standardese mumbo jumbo translates to is that when you're doing &member inside a const method it's really &(this->member) and cv-qualifiers (const and volatile) for the resulting pointer are taken from the object this points to. Since you're inside a const method this points to a const object in that scope and so & returns a pointer to const member.

        Btw. I've seen an interesting debate somewhere (can't find it now, it was a while ago) about if this should be a const pointer to const object or just a pointer to const object i.e. T const * vs T const * const. The argument for non-const this pointer was some wizardry with modifying this inside a member to avoid vtables. It landed on this being a non-const prvalue and thus non-assignable, but those are some deep trenches :)

        @Christian-Ehrlicher said in Return pointer-to-member in const method:

        If you want some further discussion points, take a look at how the Qt api returns pointers:

        I believe the last two are just straight pointer retrievals so not a big deal. The first one needs that branching logic I mentioned so it's basically against all I've said, but that's a design choice Qt takes. It is well known to take small performance hits here and there for the sake of ease of use and I think it's a fair compromise for all that it offers in return - consistency being a big one. Not a design I would make but hey, can't have it all the way I like :)

        1 Reply Last reply
        3
        • J Offline
          J Offline
          JonB
          wrote on 29 Oct 2020, 19:37 last edited by JonB
          #22

          @Christian-Ehrlicher , @Chris-Kawa

          QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
          

          Does the QLayoutItem* returned here point to a member variable of the QGridLayout? If it does, then that's what I want to achieve.

          1 Reply Last reply
          0
          • C Offline
            C Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 29 Oct 2020, 19:44 last edited by
            #23

            @JonB Not really. It's more like QGridLayout has a container of QLayoutItem*s, not QLayoutItems. The container is const and the pointers become const but they don't point to const things. The pointer itself is basically copied on return so there's no problem with returning a non-const pointer. It's a by value return and you can copy a const value to non-const object no problem.

            J 1 Reply Last reply 29 Oct 2020, 19:48
            0
            • C Chris Kawa
              29 Oct 2020, 19:44

              @JonB Not really. It's more like QGridLayout has a container of QLayoutItem*s, not QLayoutItems. The container is const and the pointers become const but they don't point to const things. The pointer itself is basically copied on return so there's no problem with returning a non-const pointer. It's a by value return and you can copy a const value to non-const object no problem.

              J Offline
              J Offline
              JonB
              wrote on 29 Oct 2020, 19:48 last edited by
              #24

              @Chris-Kawa said in Return pointer-to-member in const method:

              The container is const and the pointers become const but they don't point to const things

              :)

              Yeah, so what you're really saying is: you need to cheat/go complex like them if you want to achieve this. No, I do get it. There isn't, and isn't supposed to be, a neat, simple way to do what I want (obtain this behaviour on a straightforward member).

              1 Reply Last reply
              0
              • F Offline
                F Offline
                fcarney
                wrote on 29 Oct 2020, 19:51 last edited by
                #25

                Heh, maybe C++ needs a permission system similar to *nix filesystems?

                C++ is a perfectly valid school of magic.

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SimonSchroeder
                  wrote on 30 Oct 2020, 08:20 last edited by
                  #26

                  Maybe to answer a few questions (as short as possible).

                  1. Yes, it is good practice to overload your methods for const, just as you described:
                  int *pointerToMember() { return &member; }
                  const int *pointerToMember() const { return &member; }
                  
                  1. The problem to reimplementing the const and non-const version is quite old. The standard book on these kind of problems is "Effective C++" by Scott Meyers. I found these answers on StackOverflow referencing this book for this problem:
                    https://stackoverflow.com/questions/856542/elegant-solution-to-duplicate-const-and-non-const-getters
                    https://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func/123995
                  2. How to select on implementation over the other? If you put const after a method declaration like this:
                    const int *pointerToMember() const { return &member; }
                    it means that the this pointer is const. This is why you should use const correctness throughout your entire program. Then you don't have to think about which version you should select. If your object (or pointer/reference to object) is const, you can only call const-method and thus never change the object. If your object (or pointer/reference to object) is non-const, it has the right to change. This means the following for your control:
                  Foo &o1 = getObjectFromSomewhere();  // non-const object => changes allowed
                  o1->pointerToMember();               // o1 is non-const => this-pointer to pointerToMember() is non-const
                                                       // => call non-const method
                  const Foo &o2 = getObjectFromSomewhere(); // I know I don't want to change anything => get only const-reference
                  o2->pointerToMember();                    // o2 is const => this-pointer to pointerToMember() is const
                                                            // => call to const method
                  
                  // force const method for o1 as well
                  const_cast<const Foo&>(o1)->pointerToMember();
                  

                  I guess this would be proper C++. I tend to write const as often as possible and only leave it out if I want to change an object.

                  I suggest reading Scott Meyers' books on effective C++.

                  C J 2 Replies Last reply 30 Oct 2020, 08:38
                  0
                  • S SimonSchroeder
                    30 Oct 2020, 08:20

                    Maybe to answer a few questions (as short as possible).

                    1. Yes, it is good practice to overload your methods for const, just as you described:
                    int *pointerToMember() { return &member; }
                    const int *pointerToMember() const { return &member; }
                    
                    1. The problem to reimplementing the const and non-const version is quite old. The standard book on these kind of problems is "Effective C++" by Scott Meyers. I found these answers on StackOverflow referencing this book for this problem:
                      https://stackoverflow.com/questions/856542/elegant-solution-to-duplicate-const-and-non-const-getters
                      https://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func/123995
                    2. How to select on implementation over the other? If you put const after a method declaration like this:
                      const int *pointerToMember() const { return &member; }
                      it means that the this pointer is const. This is why you should use const correctness throughout your entire program. Then you don't have to think about which version you should select. If your object (or pointer/reference to object) is const, you can only call const-method and thus never change the object. If your object (or pointer/reference to object) is non-const, it has the right to change. This means the following for your control:
                    Foo &o1 = getObjectFromSomewhere();  // non-const object => changes allowed
                    o1->pointerToMember();               // o1 is non-const => this-pointer to pointerToMember() is non-const
                                                         // => call non-const method
                    const Foo &o2 = getObjectFromSomewhere(); // I know I don't want to change anything => get only const-reference
                    o2->pointerToMember();                    // o2 is const => this-pointer to pointerToMember() is const
                                                              // => call to const method
                    
                    // force const method for o1 as well
                    const_cast<const Foo&>(o1)->pointerToMember();
                    

                    I guess this would be proper C++. I tend to write const as often as possible and only leave it out if I want to change an object.

                    I suggest reading Scott Meyers' books on effective C++.

                    C Offline
                    C Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 30 Oct 2020, 08:38 last edited by
                    #27

                    @SimonSchroeder said:

                    const int *pointerToMember() const { return &member; }
                    it means that the this pointer is const

                    No, it's not ;) The object it points to is const. As I mentioned earlier the pointer itself is not.

                    // force const method for o1 as well
                    const_cast<const Foo&>(o1)->pointerToMember();

                    A more semantic (and shorter) way of writing this in modern C++ is using std::as_const or qAsConst in Qt, which do the same thing, just doesn't look as hacky.

                    1 Reply Last reply
                    5
                    • S SimonSchroeder
                      30 Oct 2020, 08:20

                      Maybe to answer a few questions (as short as possible).

                      1. Yes, it is good practice to overload your methods for const, just as you described:
                      int *pointerToMember() { return &member; }
                      const int *pointerToMember() const { return &member; }
                      
                      1. The problem to reimplementing the const and non-const version is quite old. The standard book on these kind of problems is "Effective C++" by Scott Meyers. I found these answers on StackOverflow referencing this book for this problem:
                        https://stackoverflow.com/questions/856542/elegant-solution-to-duplicate-const-and-non-const-getters
                        https://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func/123995
                      2. How to select on implementation over the other? If you put const after a method declaration like this:
                        const int *pointerToMember() const { return &member; }
                        it means that the this pointer is const. This is why you should use const correctness throughout your entire program. Then you don't have to think about which version you should select. If your object (or pointer/reference to object) is const, you can only call const-method and thus never change the object. If your object (or pointer/reference to object) is non-const, it has the right to change. This means the following for your control:
                      Foo &o1 = getObjectFromSomewhere();  // non-const object => changes allowed
                      o1->pointerToMember();               // o1 is non-const => this-pointer to pointerToMember() is non-const
                                                           // => call non-const method
                      const Foo &o2 = getObjectFromSomewhere(); // I know I don't want to change anything => get only const-reference
                      o2->pointerToMember();                    // o2 is const => this-pointer to pointerToMember() is const
                                                                // => call to const method
                      
                      // force const method for o1 as well
                      const_cast<const Foo&>(o1)->pointerToMember();
                      

                      I guess this would be proper C++. I tend to write const as often as possible and only leave it out if I want to change an object.

                      I suggest reading Scott Meyers' books on effective C++.

                      J Offline
                      J Offline
                      JonB
                      wrote on 30 Oct 2020, 08:43 last edited by
                      #28

                      @SimonSchroeder
                      I read the two links on stackoverflow. Both of them, and that guy's book, came up with what I have come to from @jsulm's solution above:

                      int *pointerToMember()  { const MyClass *_this = this;  return const_cast<int*>(_this->pointerToMember()); } 
                      // or
                      int *pointerToMember()  { return const_cast<int*>( const_cast<const MyClass *>(this)->pointerToMember() ); }
                      

                      So I am a happy bunny, within the bounds of C++ obscure-readability :)

                      1 Reply Last reply
                      0
                      • J jsulm
                        29 Oct 2020, 13:33

                        @JonB It's getting ugly :-)

                        const int *pointerToMember() const { return &member; }
                        int *pointerToMember()  { const MyClass *_this = this;  return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
                        
                        J Offline
                        J Offline
                        JonB
                        wrote on 2 Nov 2020, 15:32 last edited by JonB 11 Feb 2020, 15:38
                        #29

                        Dear @jsulm
                        I am now having to unmark your proposal of:

                        const int *pointerToMember() const { return &member; }
                        int *pointerToMember()  { const MyClass *_this = this;  return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
                        

                        as acceptable here. All because of https://forum.qt.io/topic/120489/qvector-one-line-deep-copy/16, where it turned out to cause me horrible grief :)

                        My situation is like:

                        const Class::MyStruct *Class::find(int arg) const
                        {
                            for (const MyStruct &ms : current)
                                if (ms.arg== arg)
                                    return &ms;
                            return nullptr;
                        }
                        
                        Class::MyStruct *Class::find(int arg)
                        {
                            const Class *_this = this;
                            return const_cast<MyStruct *>(_this->find(arg));
                        }
                        
                        QVector<MyStruct> current, saved;  // member variables
                        current.append(...);  // this can be called at various times
                        saved = current;  // this can be called at various times
                        
                        MyStruct *ms = find(something);  // this will be found in current
                        if (ms != nullptr)
                            ms->someMember = newValue;  // want to change in current, only
                                // but it doesn't, it *also* means it has changed in saved too
                                // because this fails to cause a "copy-on-write"
                                // as a consequence (apparently) of the const_cast<> in the "writeable" find()
                        

                        So my actual pointerToMember() needs to return a pointer to an element in a member QVector. That must be allowed, but your proposal "breaks" Qt's shared-value copy-on-write behaviour, as described in the other thread.

                        So now what do you propose for a "safe" solution here? :)

                        S 1 Reply Last reply 3 Nov 2020, 08:16
                        0
                        • C Offline
                          C Offline
                          Christian Ehrlicher
                          Lifetime Qt Champion
                          wrote on 2 Nov 2020, 16:04 last edited by Christian Ehrlicher 11 Feb 2020, 16:15
                          #30

                          @JonB said in Return pointer-to-member in const method:

                          So now what do you propose for a "safe" solution here?

                          • Implement the non-const version and call it in the const version (but may lead to an unneeded detach)
                          • implement it twice
                          • don't use a cow container
                          • use a template:
                          struct s
                          {
                              int one = 1;
                              int two = 2;
                          };
                          
                          class foo
                          {
                          public:
                              s* getFoo(int idx) { return getFooInternal<s*>(this, idx); }
                              const s* getFoo(int idx) const { return getFooInternal<const s*>(this, idx); }
                          private:
                              template <typename T, typename F>
                              static T getFooInternal(F *f, int idx)
                              {
                                  return &f->m_foo[idx];
                              }
                              QVector<s> m_foo;
                          };
                          

                          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                          Visit the Qt Academy at https://academy.qt.io/catalog

                          J 1 Reply Last reply 2 Nov 2020, 16:23
                          1
                          • C Christian Ehrlicher
                            2 Nov 2020, 16:04

                            @JonB said in Return pointer-to-member in const method:

                            So now what do you propose for a "safe" solution here?

                            • Implement the non-const version and call it in the const version (but may lead to an unneeded detach)
                            • implement it twice
                            • don't use a cow container
                            • use a template:
                            struct s
                            {
                                int one = 1;
                                int two = 2;
                            };
                            
                            class foo
                            {
                            public:
                                s* getFoo(int idx) { return getFooInternal<s*>(this, idx); }
                                const s* getFoo(int idx) const { return getFooInternal<const s*>(this, idx); }
                            private:
                                template <typename T, typename F>
                                static T getFooInternal(F *f, int idx)
                                {
                                    return &f->m_foo[idx];
                                }
                                QVector<s> m_foo;
                            };
                            
                            J Offline
                            J Offline
                            JonB
                            wrote on 2 Nov 2020, 16:23 last edited by JonB 11 Feb 2020, 16:28
                            #31

                            @Christian-Ehrlicher said in Return pointer-to-member in const method:

                            Implement the non-const version and call it in the const version (but may lead to an unneeded detach)

                            Yes, I will think about that. I never cared about copy-on-write in this function's case.

                            implement it twice

                            LOL. That's not a solution, it's a workaround! You saw my lookup code, I'm not duplicating that!

                            don't use a cow container

                            No cows anywhere in my code....? Oohhhh, sorry, got it...

                            use a template:

                            I will indeed look at your code tomorrow, I had a feeling templates might come into it.

                            TBH, all I really want here, when I think about, is to be allowed to call a non-const member method from a const member function, in this case. My non-const function doesn't alter anything --- only returns a pointer to internal which might be used to write into by caller. But I won't be doing any such thing when calling from a const member. I (think I) want a new semi_const keyword, at least for a method, which does just promise not to alter the state of *this. That's all I thought const method() did when I started this thread. Is that so much to ask for? :)

                            C 1 Reply Last reply 2 Nov 2020, 16:43
                            0
                            • J JonB
                              2 Nov 2020, 16:23

                              @Christian-Ehrlicher said in Return pointer-to-member in const method:

                              Implement the non-const version and call it in the const version (but may lead to an unneeded detach)

                              Yes, I will think about that. I never cared about copy-on-write in this function's case.

                              implement it twice

                              LOL. That's not a solution, it's a workaround! You saw my lookup code, I'm not duplicating that!

                              don't use a cow container

                              No cows anywhere in my code....? Oohhhh, sorry, got it...

                              use a template:

                              I will indeed look at your code tomorrow, I had a feeling templates might come into it.

                              TBH, all I really want here, when I think about, is to be allowed to call a non-const member method from a const member function, in this case. My non-const function doesn't alter anything --- only returns a pointer to internal which might be used to write into by caller. But I won't be doing any such thing when calling from a const member. I (think I) want a new semi_const keyword, at least for a method, which does just promise not to alter the state of *this. That's all I thought const method() did when I started this thread. Is that so much to ask for? :)

                              C Offline
                              C Offline
                              Christian Ehrlicher
                              Lifetime Qt Champion
                              wrote on 2 Nov 2020, 16:43 last edited by
                              #32

                              Simplified it the template little bit more:

                              class foo
                              {
                                  template <typename F>
                                  static auto getFooInternal(F *f, int idx)
                                  {
                                      return &f->m_foo[idx];
                                  }
                              public:
                                  s* getFoo(int idx) { return getFooInternal(this, idx); }
                                  const s* getFoo(int idx) const { return getFooInternal(this, idx); }
                              private:
                                  QVector<s> m_foo;
                              };
                              

                              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                              Visit the Qt Academy at https://academy.qt.io/catalog

                              1 Reply Last reply
                              1
                              • J JonB
                                2 Nov 2020, 15:32

                                Dear @jsulm
                                I am now having to unmark your proposal of:

                                const int *pointerToMember() const { return &member; }
                                int *pointerToMember()  { const MyClass *_this = this;  return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
                                

                                as acceptable here. All because of https://forum.qt.io/topic/120489/qvector-one-line-deep-copy/16, where it turned out to cause me horrible grief :)

                                My situation is like:

                                const Class::MyStruct *Class::find(int arg) const
                                {
                                    for (const MyStruct &ms : current)
                                        if (ms.arg== arg)
                                            return &ms;
                                    return nullptr;
                                }
                                
                                Class::MyStruct *Class::find(int arg)
                                {
                                    const Class *_this = this;
                                    return const_cast<MyStruct *>(_this->find(arg));
                                }
                                
                                QVector<MyStruct> current, saved;  // member variables
                                current.append(...);  // this can be called at various times
                                saved = current;  // this can be called at various times
                                
                                MyStruct *ms = find(something);  // this will be found in current
                                if (ms != nullptr)
                                    ms->someMember = newValue;  // want to change in current, only
                                        // but it doesn't, it *also* means it has changed in saved too
                                        // because this fails to cause a "copy-on-write"
                                        // as a consequence (apparently) of the const_cast<> in the "writeable" find()
                                

                                So my actual pointerToMember() needs to return a pointer to an element in a member QVector. That must be allowed, but your proposal "breaks" Qt's shared-value copy-on-write behaviour, as described in the other thread.

                                So now what do you propose for a "safe" solution here? :)

                                S Offline
                                S Offline
                                SimonSchroeder
                                wrote on 3 Nov 2020, 08:16 last edited by
                                #33

                                @JonB said in Return pointer-to-member in const method:

                                MyStruct *ms = find(something);  // this will be found in current
                                if (ms != nullptr)
                                    ms->someMember = newValue;  // want to change in current, only
                                        // but it doesn't, it *also* means it has changed in saved too
                                        // because this fails to cause a "copy-on-write"
                                        // as a consequence (apparently) of the const_cast<> in the "writeable" find()
                                

                                I believe this is not how copy-on-write for QVector works. (Can someone back me up or correct me on this?) I don't know of any mechanism in C++ which would allow to monitor changes to memory. ms->someMember = newValue; will not, in my understanding, trigger a copy of the vector. Appending, inserting, removing, etc. will trigger a copy. I am not certain if operator[](int) without const would trigger a copy. In this case you should implement Class::find twice because then for(const MyStruct &ms : current) and for(Mystruct &ms : current) would behave differently.

                                I would usually return a reference instead of the pointer. Then, the template trick by @Christian-Ehrlicher would help:

                                template<class T>
                                T Class::find(int arg) // has to be static
                                {
                                    for(T ms : current)
                                    ...
                                }
                                

                                Your implementations could then call find<const MyStruct&>(arg) and find<MyStruct&>(arg). With a small change, this also works with your pointer:

                                template<class T>
                                T *Class:find(int arg) // still static
                                {
                                    for(T &ms : current)
                                    ...
                                }
                                
                                // calls:
                                find<const MyStruct>(arg);
                                find<MyStruct>(arg);
                                

                                Furthermore, it is very common to have a QVector<MyStruct*> instead of QVector<MyStruct>. This will further decouple copy-on-write for the QVector. The major reason to store a pointer instead of the object is to still have polymorphism and being able to have inherited objects inside your QVector, as well. Another reason would be if your objects are quite large. Expanding the vector would be slower because the whole objects instead of just pointers would need to be copied.

                                J 1 Reply Last reply 3 Nov 2020, 10:46
                                0
                                • S SimonSchroeder
                                  3 Nov 2020, 08:16

                                  @JonB said in Return pointer-to-member in const method:

                                  MyStruct *ms = find(something);  // this will be found in current
                                  if (ms != nullptr)
                                      ms->someMember = newValue;  // want to change in current, only
                                          // but it doesn't, it *also* means it has changed in saved too
                                          // because this fails to cause a "copy-on-write"
                                          // as a consequence (apparently) of the const_cast<> in the "writeable" find()
                                  

                                  I believe this is not how copy-on-write for QVector works. (Can someone back me up or correct me on this?) I don't know of any mechanism in C++ which would allow to monitor changes to memory. ms->someMember = newValue; will not, in my understanding, trigger a copy of the vector. Appending, inserting, removing, etc. will trigger a copy. I am not certain if operator[](int) without const would trigger a copy. In this case you should implement Class::find twice because then for(const MyStruct &ms : current) and for(Mystruct &ms : current) would behave differently.

                                  I would usually return a reference instead of the pointer. Then, the template trick by @Christian-Ehrlicher would help:

                                  template<class T>
                                  T Class::find(int arg) // has to be static
                                  {
                                      for(T ms : current)
                                      ...
                                  }
                                  

                                  Your implementations could then call find<const MyStruct&>(arg) and find<MyStruct&>(arg). With a small change, this also works with your pointer:

                                  template<class T>
                                  T *Class:find(int arg) // still static
                                  {
                                      for(T &ms : current)
                                      ...
                                  }
                                  
                                  // calls:
                                  find<const MyStruct>(arg);
                                  find<MyStruct>(arg);
                                  

                                  Furthermore, it is very common to have a QVector<MyStruct*> instead of QVector<MyStruct>. This will further decouple copy-on-write for the QVector. The major reason to store a pointer instead of the object is to still have polymorphism and being able to have inherited objects inside your QVector, as well. Another reason would be if your objects are quite large. Expanding the vector would be slower because the whole objects instead of just pointers would need to be copied.

                                  J Offline
                                  J Offline
                                  JonB
                                  wrote on 3 Nov 2020, 10:46 last edited by JonB 11 Mar 2020, 10:49
                                  #34

                                  @SimonSchroeder
                                  Hi Simon,

                                  Several points from you, thank you, let me address a couple of them.

                                  I believe this is not how copy-on-write for QVector works. (Can someone back me up or correct me on this?) I don't know of any mechanism in C++ which would allow to monitor changes to memory. ms->someMember = newValue; will not, in my understanding, trigger a copy of the vector.

                                  I never said it would! It doesn't. What I said was

                                  as a consequence (apparently) of the const_cast<> in the "writeable" find()

                                  You have to remember the find() method did its work my moving through current by reference. I expected the CoW to have occurred during that, then my assignment would have only affected current. But it didn't CoW. And the reason for that is in the two definitions of find() given to me by Mr @jsulm. Which I liked, and thought would work, but fails in this situation. It would have worked if the "writeable" definition went for (MyStruct &ms : current), while the "read-only" one went for (const MyStruct &ms : current). But because instead it uses only the latter, const one to search, and then goes return const_cast<MyStruct *>(_this->find(arg));, this breaks my expected CoW.

                                  In fact, if as @Christian-Ehrlicher said earlier:

                                  Implement the non-const version and call it in the const version (but may lead to an unneeded detach)

                                  I reverse code, so that the "writeable" one does for (MyStruct &ms : current) (which will CoW) and make the read-only one call that, it does work. But, as he observes, that is inefficient insofar as it CoWs everything even in the read-only case.

                                  I am not certain if operator[](int) without const would trigger a copy

                                  It does. I stated that even just putting a watch on current[something] in the Debugger pane is enough to trigger the copy!

                                  In this case you should implement Class::find twice because then for(const MyStruct &ms : current) and for(Mystruct &ms : current) would behave differently.

                                  Yes, that makes it work, but that is what I am asking to avoid! Personally --- maybe not you --- I am not happy implementing a method body twice --- it can be quite a few lines of code --- in order to deal with the vagaries of const. It leads to code-bloat and potential maintenance/bug problems. The algorithm is identical, I should not "have to" write two definitions to keep const happy. Just my 2 cents. But it's what I am interested in the question.

                                  I would usually return a reference instead of the pointer.

                                  As I wrote earlier, the need for pointer and not reference is that the find() absolutely can fail to find the match, and so has to be able to return nullptr, which is why I wrote it that way. Tell me how a reference solution allows for that?

                                  FTR: At the time I wrote the find() there was no second copy/reference to the vector. Everything worked fine. Only as I expanded and found I needed a separate copy did the CoW problem rear its head.

                                  1 Reply Last reply
                                  0
                                  • C Offline
                                    C Offline
                                    Christian Ehrlicher
                                    Lifetime Qt Champion
                                    wrote on 3 Nov 2020, 19:03 last edited by
                                    #35

                                    Use my template, it's working as expected :)

                                    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                    Visit the Qt Academy at https://academy.qt.io/catalog

                                    1 Reply Last reply
                                    0

                                    28/35

                                    30 Oct 2020, 08:43

                                    • Login

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