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

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.
  • F Offline
    F Offline
    fcarney
    wrote on 29 Oct 2020, 14:56 last edited by
    #10
    const auto value1 = obj->pointerToMember(); // should be const version
    auto value2 = obj->pointerToMember(); // should be non-const version
    

    C++ is a perfectly valid school of magic.

    J 1 Reply Last reply 29 Oct 2020, 16:32
    0
    • F fcarney
      29 Oct 2020, 14:56
      const auto value1 = obj->pointerToMember(); // should be const version
      auto value2 = obj->pointerToMember(); // should be non-const version
      
      J Offline
      J Offline
      JonB
      wrote on 29 Oct 2020, 16:32 last edited by JonB
      #11

      @fcarney

          const int *pointerToMember() const { ... }
          int *pointerToMember()  { ... }
      
      const auto value1 = obj->pointerToMember(); // should be const version
      

      Nope (but thanks for your interest!), that const one steps straight into the second, non-const variant! (As does the other call too.)

      I would not expect const variable = method() to call the const variant of the method, particularly. So far as I know, that const on the variable perfectly allows it to pick any non-const method, it only chooses to treat the return result as const but no other effect. Which is what it does it here.

      1 Reply Last reply
      1
      • F Offline
        F Offline
        fcarney
        wrote on 29 Oct 2020, 16:55 last edited by
        #12

        Interesting, maybe don't overload then. I tried as reference to see if it makes a difference. No change.

        C++ is a perfectly valid school of magic.

        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 29 Oct 2020, 17:03 last edited by JonB
          #13

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

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

          I wrote

          Now I have an extra assignment to slow my code down :( ;-)

          So I adapted, to remove _this:

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

          I prefer yours for readability :)

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

            I left you for one minute and there's const_cast and goto :P

            Getting back to original problem. At first glance you could do something like this:

            class Foo
            {
            private:
                std::array<Stuff, 42> data;
                int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
             
            public:
                Stuff* get(Key key) { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; }
                const Stuff* get(Key key) const { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; }
            };
            

            no casts but I would argue that this is a patch work. The solution becomes a lot nicer if you dig into the problem, which is you're trying to pack two things into one getter - a logic to determine if given element is present and retrieving it. Those are two things and they also incur a performance cost (branching) so I'd say design your interface so that the decision about taking the cost or not is left to the user of your class:

            class Bar
            {
            private:
                std::array<Stuff, 42> data;
                
            public:
                int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                Stuff& at(int index) { return data[index]; }
                const Stuff& at(int index) const { return data[index]; }
            };
            

            Shorter, easier, faster and class doesn't absorb responsibilities it doesn't need to. Also those at methods will most definitely get inlined and disappear (inlining is real and super important, don't dismiss it :) )

            As for the other thing:

            const auto value1 = obj->pointerToMember(); // should be const version
            auto value2 = obj->pointerToMember(); // should be non-const version

            const or non-const is determined by the constness of the object it is called on, not unrelated variable the result is assigned to, so:

            auto value1 = const_obj->pointerToMember(); // const version, auto resolves to const something
            auto value2 = obj->pointerToMember(); // non-const version, auto resolves to non-const something
            

            Btw. this is a source of a common performance trap with Qt and C++11:

            QVector<Stuff> stuff;
            
            for (Stuff& foo : stuff) {}  //no! calls non-const begin/end and can cause expensive detach()
            

            so people think "oh, I should just add const and it's fine":

            for (const Stuff& foo : stuff) {}  //no! still calls non-const begin/end
            

            The proper way to do it is:

            for (const Stuff& foo : qAsConst(stuff)) {}  //ok, calls const begin/end
            
            J 1 Reply Last reply 29 Oct 2020, 17:26
            5
            • C Chris Kawa
              29 Oct 2020, 17:04

              I left you for one minute and there's const_cast and goto :P

              Getting back to original problem. At first glance you could do something like this:

              class Foo
              {
              private:
                  std::array<Stuff, 42> data;
                  int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
               
              public:
                  Stuff* get(Key key) { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; }
                  const Stuff* get(Key key) const { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; }
              };
              

              no casts but I would argue that this is a patch work. The solution becomes a lot nicer if you dig into the problem, which is you're trying to pack two things into one getter - a logic to determine if given element is present and retrieving it. Those are two things and they also incur a performance cost (branching) so I'd say design your interface so that the decision about taking the cost or not is left to the user of your class:

              class Bar
              {
              private:
                  std::array<Stuff, 42> data;
                  
              public:
                  int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                  Stuff& at(int index) { return data[index]; }
                  const Stuff& at(int index) const { return data[index]; }
              };
              

              Shorter, easier, faster and class doesn't absorb responsibilities it doesn't need to. Also those at methods will most definitely get inlined and disappear (inlining is real and super important, don't dismiss it :) )

              As for the other thing:

              const auto value1 = obj->pointerToMember(); // should be const version
              auto value2 = obj->pointerToMember(); // should be non-const version

              const or non-const is determined by the constness of the object it is called on, not unrelated variable the result is assigned to, so:

              auto value1 = const_obj->pointerToMember(); // const version, auto resolves to const something
              auto value2 = obj->pointerToMember(); // non-const version, auto resolves to non-const something
              

              Btw. this is a source of a common performance trap with Qt and C++11:

              QVector<Stuff> stuff;
              
              for (Stuff& foo : stuff) {}  //no! calls non-const begin/end and can cause expensive detach()
              

              so people think "oh, I should just add const and it's fine":

              for (const Stuff& foo : stuff) {}  //no! still calls non-const begin/end
              

              The proper way to do it is:

              for (const Stuff& foo : qAsConst(stuff)) {}  //ok, calls const begin/end
              
              J Offline
              J Offline
              JonB
              wrote on 29 Oct 2020, 17:26 last edited by JonB
              #15

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

              I left you for one minute and there's const_cast and goto :P

              That's what happens to fluffy kittens if you turn your back....

              Let's pick my one of what (I understand) you suggest:

                  int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                  Stuff& at(int index) { return data[index]; }
                  const Stuff& at(int index) const { return data[index]; }
              

              So, you avoid the problem by making the lookup function return an int index. Then you return const/non-const data[index].

              This breaks my (unspoken) "efficiency" criterion! My lookup marches through the array without an int counter, only with a pointer (it can return nullptr on not-found, so no references here), and returns that. You will make me turn that into an index, and then you will turn it back by adding it onto data.

              I am shocked. I was speaking to someone recently in another thread here, and they berated me for the overhead of indexing into arrays, when I said it was "milliseconds" they said "every microsecond counts, in game development, this is not to be ignored". Can you think who that was? :D

              C 1 Reply Last reply 29 Oct 2020, 17:32
              0
              • J JonB
                29 Oct 2020, 17:26

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

                I left you for one minute and there's const_cast and goto :P

                That's what happens to fluffy kittens if you turn your back....

                Let's pick my one of what (I understand) you suggest:

                    int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                    Stuff& at(int index) { return data[index]; }
                    const Stuff& at(int index) const { return data[index]; }
                

                So, you avoid the problem by making the lookup function return an int index. Then you return const/non-const data[index].

                This breaks my (unspoken) "efficiency" criterion! My lookup marches through the array without an int counter, only with a pointer (it can return nullptr on not-found, so no references here), and returns that. You will make me turn that into an index, and then you will turn it back by adding it onto data.

                I am shocked. I was speaking to someone recently in another thread here, and they berated me for the overhead of indexing into arrays, when I said it was "milliseconds" they said "every microsecond counts, in game development, this is not to be ignored". Can you think who that was? :D

                C Offline
                C Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on 29 Oct 2020, 17:32 last edited by
                #16

                @JonB said:

                Can you think who that was? :D

                Yup, and I stand by what I said, which was not indexing into an array but indexing into a vector. Huge huge difference.

                The int index is just an example. Every case is different and it doesn't have to be an int. It could be an iterator, some hash thingie or whatever is most efficient in your case. Pointer has that nasty proprty of being both index and data at the same time, which causes your problems - you want a const pointy thing and a non-const data thing. One variable can't be both at the same time so that's why I'm suggesting to split them up.

                J 1 Reply Last reply 29 Oct 2020, 17:48
                2
                • C Chris Kawa
                  29 Oct 2020, 17:32

                  @JonB said:

                  Can you think who that was? :D

                  Yup, and I stand by what I said, which was not indexing into an array but indexing into a vector. Huge huge difference.

                  The int index is just an example. Every case is different and it doesn't have to be an int. It could be an iterator, some hash thingie or whatever is most efficient in your case. Pointer has that nasty proprty of being both index and data at the same time, which causes your problems - you want a const pointy thing and a non-const data thing. One variable can't be both at the same time so that's why I'm suggesting to split them up.

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

                  @Chris-Kawa

                  int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                  

                  The problem here is, that function returning int does not exhibit the problem! You don't have to worry about returning int versus const int. It's when function returns a something *. int method() const is never a problem, but int *method() const where it returns a member variable is a problem. So I see I then need const int *method() const as well as int *method(). Once your indexOf() returns a pointer into a member variable thingie you end up needing a const something *indexOf() const and a something *indexOf(Key key), for a method which does the same thing.

                  Anyways. My head is hurting on this. We may all be saying the same thing in different ways.

                  C 1 Reply Last reply 29 Oct 2020, 18:12
                  0
                  • J JonB
                    29 Oct 2020, 17:48

                    @Chris-Kawa

                    int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
                    

                    The problem here is, that function returning int does not exhibit the problem! You don't have to worry about returning int versus const int. It's when function returns a something *. int method() const is never a problem, but int *method() const where it returns a member variable is a problem. So I see I then need const int *method() const as well as int *method(). Once your indexOf() returns a pointer into a member variable thingie you end up needing a const something *indexOf() const and a something *indexOf(Key key), for a method which does the same thing.

                    Anyways. My head is hurting on this. We may all be saying the same thing in different ways.

                    C Offline
                    C Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 29 Oct 2020, 18:12 last edited by
                    #18

                    @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 1 Reply Last reply 29 Oct 2020, 18:32
                    2
                    • 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

                                          19/35

                                          29 Oct 2020, 18:32

                                          • Login

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