const/non const member function vs. templates

Discussion in 'C++' started by Markus Keppeler, Jul 26, 2011.

  1. Hi Together!

    In some (partly legacy) code I have member functions like this:



    // class Dummy (class of any type).
    class XY
    {
    Dummy *pGetMe( (returns pointer to some
    ); dummy member orwhatever)

    const Dummy *pGetMe( (returns const pointer to some
    ) const; dummy member or whatever).
    }



    Since the implementations on both pGetMe's are 100% identically, only
    that they use const/non const types/functions, I'd like to merge them.

    I cannot implement the non-const version by calling down to the const
    one, since at some level it needs to fetch the non-const pointer to
    something. Here also some legacy-issues come into, so I cannot
    refactor the entire design ;-).

    My next thought was to use templates, like

    template <class T>
    T pGetMe();

    , but here I don't know how to deal with the const/non constness of
    the member function. Can I add the const/non const based on the type
    of T?

    any help is appreciated,
    Markus

    --
    Nur tote Fische schwimmen mit dem Strom.
    Markus Keppeler, Jul 26, 2011
    #1
    1. Advertising

  2. Markus Keppeler

    Virchanza Guest

    On Jul 26, 1:58 pm, Markus Keppeler <> wrote:

    > class XY
    > {
    >    Dummy *pGetMe(            (returns pointer to some
    >    );                        dummy member orwhatever)
    >
    >    const Dummy *pGetMe(      (returns const pointer to some
    >    ) const;                  dummy member or whatever).
    >
    > }
    >



    Just one way of going about this:

    Dummy const *pGetMe() const
    {
    return address_of_member_data; // or whatever else it does
    }

    Dummy *pGetMe()
    {
    return const_cast<Dummy*>( const_cast<XY const *>(this)-
    >pGetMe() );

    }
    Virchanza, Jul 26, 2011
    #2
    1. Advertising

  3. On 7/26/2011 9:12 AM, Virchanza wrote:
    > On Jul 26, 1:58 pm, Markus Keppeler<> wrote:
    >
    >> class XY
    >> {
    >> Dummy *pGetMe( (returns pointer to some
    >> ); dummy member orwhatever)
    >>
    >> const Dummy *pGetMe( (returns const pointer to some
    >> ) const; dummy member or whatever).
    >>
    >> }
    >>

    >
    >
    > Just one way of going about this:
    >
    > Dummy const *pGetMe() const
    > {
    > return address_of_member_data; // or whatever else it does
    > }
    >
    > Dummy *pGetMe()
    > {
    > return const_cast<Dummy*>( const_cast<XY const *>(this)-
    >> pGetMe() );

    > }


    Actually, I'd consider implementing them in reverse. Think about it.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jul 26, 2011
    #3
  4. First of all: Thanks for the reply :).

    Am 26.07.2011 15:12, schrieb Virchanza:
    > Just one way of going about this:
    >
    > [...]
    > return const_cast<Dummy*>( const_cast<XY const *>(this)->>pGetMe() );


    I agree that a const_cast might be a solution here. But the example
    was only a very small and trivial one, I'm dealing with more complex
    ones where a const_cast needs more explizit work to be done additionally.

    Secondly, I don't like const_cast at all, but that is only my personal
    flavor ;-).

    Thats why I was curious about any template mechanism that might solve
    this more elegant (though I doubt that such one exists :-( ).

    thx,
    Markus

    --
    Nur tote Fische schwimmen mit dem Strom.
    Markus Keppeler, Jul 26, 2011
    #4
  5. Markus Keppeler

    Balog Pal Guest

    "Markus Keppeler" <> az alábbiakat írta a következo
    hírüzenetben: 4e2ecb09$0$6639$-online.net...
    > First of all: Thanks for the reply :).
    >
    > Am 26.07.2011 15:12, schrieb Virchanza:
    >> Just one way of going about this:
    >>
    >> [...]
    >> return const_cast<Dummy*>( const_cast<XY const *>(this)->>pGetMe() );

    >
    > I agree that a const_cast might be a solution here. But the example was
    > only a very small and trivial one, I'm dealing with more complex ones
    > where a const_cast needs more explizit work to be done additionally.


    This hardy makes sense. The complexity of the work is irrelevant. With the
    above solution you will have it once, and the other function delegates to
    the single solution. You have a choice on direction, the baseline is to have
    the implementation in const and call that from non-const. With the other way
    you open the danger to change state in a const object.

    > Secondly, I don't like const_cast at all, but that is only my personal
    > flavor ;-).
    >
    > Thats why I was curious about any template mechanism that might solve this
    > more elegant (though I doubt that such one exists :-( ).


    You can have the two members and make them call a single implementation in a
    template function. That can be a private static in the class. IMO that
    solution is less elegant by far.
    Balog Pal, Jul 26, 2011
    #5
  6. Markus Keppeler

    Werner Guest

    On Jul 26, 2:58 pm, Markus Keppeler <> wrote:
    > Hi Together!
    >
    > In some (partly legacy) code I have member functions like this:
    >
    > // class Dummy     (class of any type).
    > class XY
    > {
    >    Dummy *pGetMe(            (returns pointer to some
    >    );                        dummy member orwhatever)
    >
    >    const Dummy *pGetMe(      (returns const pointer to some
    >    ) const;                  dummy member or whatever).
    >
    > }
    >
    > Since the implementations on both pGetMe's are 100% identically, only
    > that they use const/non const types/functions, I'd like to merge them.


    Implement the const one in terms of the non-const one:

    Rationale:
    - Conversion to more const is automatic and is natural.

    Note:
    - Only perform the cast if <this> is not modified during the
    applicable function.

    E.g:

    struct XY
    {
    struct Dummy
    {
    Dummy( int n ): n_( n ){}
    int n_;
    };
    Dummy d_;

    XY(): d_( 10 ){}

    Dummy *pGetMe()
    {
    return &d_;
    }

    const Dummy *pGetMe() const
    {
    return const_cast<XY*>(this)->pGetMe();
    }
    };


    int main()
    {
    const XY cxy;
    XY xy;

    XY::Dummy* d1 = xy.pGetMe();
    const XY::Dummy* d2 = cxy.pGetMe();
    std::cout << "--- The end ---" << std::endl;
    }
    Werner, Jul 26, 2011
    #6
  7. Markus Keppeler

    Werner Guest

    > On Jul 26, 2:58 pm, Markus Keppeler <> wrote:
    > --
    > Nur tote Fische schwimmen mit dem Strom.


    Net 'n onnosel mens swem teen die stroom, veral
    in die see, en dit lei meestal tot verdrinking ...
    .... 'n mens is duidelik nie 'n vis nie.

    ;-)
    Werner, Jul 26, 2011
    #7
  8. Markus Keppeler

    Virchanza Guest

    On Jul 26, 3:04 pm, Victor Bazarov <> wrote:

    > > Dummy const *pGetMe() const
    > > {
    > >      return address_of_member_data;  // or whatever else it does
    > > }

    >
    > > Dummy *pGetMe()
    > > {
    > >      return const_cast<Dummy*>( const_cast<XY const *>(this)-
    > >> pGetMe() );

    > > }

    >
    > Actually, I'd consider implementing them in reverse.  Think about it.



    Assuming that both functions perform the *exact* same
    functionality, (the only difference being that the non-const version
    returns a non-const pointer/reference), I like the idea of making the
    non-const version call the const version because then you can use the
    compiler to make sure that the shared body of code for the two
    functions doesn't alter any read-only member variables. What I mean is
    that the following problematic code would not compile if the non-const
    version called the const version:

    Dummy *pGetMe()
    {
    member_variable += 5;
    return &member_variable;
    }

    Dummy const *pGetMe() const
    {
    return const_cast<XY*>(this)->pGetMe();
    }
    Virchanza, Jul 26, 2011
    #8
  9. Hi!

    Am 26.07.2011 17:03, schrieb Balog Pal:
    >> I agree that a const_cast might be a solution here. But the example was
    >> only a very small and trivial one, I'm dealing with more complex ones
    >> where a const_cast needs more explizit work to be done additionally.

    >
    > This hardy makes sense. The complexity of the work is irrelevant. With the
    > above solution you will have it once, and the other function delegates to
    > the single solution. You have a choice on direction, the baseline is to have
    > the implementation in const and call that from non-const. With the other way
    > you open the danger to change state in a const object.


    I agree that the other way (calling non-const from const) is quite
    dangerous if the non-const changes later on.

    My problem here with the const_cast comes from the legacy. We have a
    difference in the const/non const functions for example in the way the
    the non const functions touch our internal "undo" system, whereas the
    const function doesn't need to.

    So for each const_cast I'd have to make sure to keep the undo up to
    date. This will introduce more noise than a double const/non const
    version that is reasonably short.

    thx,
    Markus

    --
    Nur tote Fische schwimmen mit dem Strom.
    Markus Keppeler, Jul 27, 2011
    #9
  10. Markus Keppeler

    Balog Pal Guest

    "Markus Keppeler" <>

    > My problem here with the const_cast comes from the legacy. We have a
    > difference in the const/non const functions for example in the way the the
    > non const functions touch our internal "undo" system, whereas the const
    > function doesn't need to.


    This doesn't map back to the original question.

    If you have a genuine difference in implementations of the two functions,
    then you are out of options. You can't meld them with either delegation or
    templates.

    > So for each const_cast I'd have to make sure to keep the undo up to date.


    That sounds like the wrong association. The undo buffer shall be tied to
    mutation itself, not constness on a pointer or reference. Why not deal with
    the undo in members of the returned thing?

    Also you suggest *diffenent* semantics of the functions with that additional
    task. So they are no longer good for overload!

    In my practice I soon dropped const overloads, and have the two functions
    with different names. (Mostly a W attached at end of non-const version.) So
    the user who has intention for modification will explicitly ask that
    version. Standing out in the code well. Signaling an error when the
    incorrect version used. And dodging the very common case where you don't
    have intention tomodify in a particular operation, but have a mutable (for
    other purposes) object around, and have the non-intended operation selected.

    > This will introduce more noise than a double const/non const version that
    > is reasonably short.


    Again, the content of the implementation is an orthogonal issue.

    I suggest to stick to the 'say what you mean, mean what you say' principle.
    Balog Pal, Jul 27, 2011
    #10
  11. On 26 Jul., Markus Keppeler wrote:
    > Hi Together!
    >
    > In some (partly legacy) code I have member functions like this:
    >
    > // class Dummy (class of any type).
    > class XY
    > {
    > Dummy *pGetMe( (returns pointer to some
    > ); dummy member orwhatever)
    >
    > const Dummy *pGetMe( (returns const pointer to some
    > ) const; dummy member or whatever).
    >
    > }
    >
    > Since the implementations on both pGetMe's are 100% identically, only
    > that they use const/non const types/functions, I'd like to merge them.
    >
    > I cannot implement the non-const version by calling down to the const
    > one, since at some level it needs to fetch the non-const pointer to
    > something. Here also some legacy-issues come into, so I cannot
    > refactor the entire design ;-).
    >
    > My next thought was to use templates, like
    >
    > template <class T>
    > T pGetMe();
    >
    > , but here I don't know how to deal with the const/non constness of
    > the member function. Can I add the const/non const based on the type
    > of T?


    The following template should do what you want, but the syntax is a
    bit different: You'd have to declare a public member Me and take the
    address of it:
    class XY
    {
    public:
    ConstCorrectAccessor<Dummy> Me;
    };

    void foo (const XY& xy)
    {
    const Dummy* OK = &xy.Me;
    Dummy* DOES_NOT_COMPILE = &xy.Me;
    }

    --------------------------------------------------------------------------------------------------------------------------------------------
    // Wrapper for plain pointers that behaves as const-correct
    // accessor.
    template<class t_Class>
    class ConstCorrectAccessor
    {
    t_Class* m_InternalPointer;
    public:
    ConstCorrectAccessor (t_Class* Pointer)
    : m_InternalPointer (Pointer)
    {}

    // Accessor methods with const-correct overload.
    const t_Class* operator-> () const {return m_InternalPointer;}
    t_Class* operator ->() {return m_InternalPointer;}

    const t_Class* operator& () const {return m_InternalPointer;}
    t_Class* operator& () {return m_InternalPointer;}
    };

    class SomeClass
    {
    public:
    void foo () const {}
    void bar () {}
    };

    class AnotherClass
    {
    public:
    ConstCorrectAccessor<SomeClass> ConstCorrectAccess;
    SomeClass* PlainPointerAccess;
    public:
    AnotherClass (SomeClass* Object)
    : PlainPointerAccess (Object),
    ConstCorrectAccess (Object)
    {}

    void test () const
    {
    ConstCorrectAccess->foo (); // OK
    //ConstCorrectAccess->bar (); // Error: Non-const method on
    SomeObject.
    PlainPointerAccess->foo (); // OK
    PlainPointerAccess->bar (); // BAD: Const-correctness not
    propagated.
    }
    };


    int main ()
    {
    SomeClass a;
    const AnotherClass b (&a);
    b.ConstCorrectAccess->foo (); // OK
    // b.ConstCorrectAccess->bar (); // Compilation error: b is const
    b.PlainPointerAccess->foo (); // OK
    b.PlainPointerAccess->bar (); // BAD: Const-correctness is not
    propagated

    AnotherClass c (&a);
    c.ConstCorrectAccess->foo (); // OK
    c.ConstCorrectAccess->bar (); // OK: c is not const
    c.PlainPointerAccess->foo (); // OK
    c.PlainPointerAccess->bar (); // OK: c is not const

    const SomeClass* constpointer2SomeClass = &b.ConstCorrectAccess;
    const SomeClass* constpointer2SomeClass2 = &c.ConstCorrectAccess;
    // SomeClass* pointer2SomeClass = &b.ConstCorrectAccess; //
    Compilation error: b is const
    SomeClass* pointer2SomeClass2 = &c.ConstCorrectAccess;

    return 0;
    }


    Regards,
    Stuart
    Stuart Redmann, Jul 28, 2011
    #11
  12. On 26 Jul., 14:58, Markus Keppeler <> wrote:
    > Hi Together!
    >
    > In some (partly legacy) code I have member functions like this:
    >
    > // class Dummy     (class of any type).
    > class XY
    > {
    >    Dummy *pGetMe(            (returns pointer to some
    >    );                        dummy member orwhatever)
    >
    >    const Dummy *pGetMe(      (returns const pointer to some
    >    ) const;                  dummy member or whatever).
    >
    > }
    >
    > Since the implementations on both pGetMe's are 100% identically, only
    > that they use const/non const types/functions, I'd like to merge them.
    >
    > I cannot implement the non-const version by calling down to the const
    > one, since at some level it needs to fetch the non-const pointer to
    > something. Here also some legacy-issues come into, so I cannot
    > refactor the entire design ;-).
    >
    > My next thought was to use templates, like
    >
    >    template <class T>
    >    T pGetMe();
    >
    > , but here I don't know how to deal with the const/non constness of
    > the member function. Can I add the const/non const based on the type
    > of T?
    >


    The following is not exactly what you want but it comes quite close:

    // Wrapper for plain pointers that behaves as const-correct
    // accessor.
    template<class t_Class>
    class ConstCorrectAccessor
    {
    t_Class* m_InternalPointer;
    public:
    ConstCorrectAccessor (t_Class* Pointer)
    : m_InternalPointer (Pointer)
    {}

    // Accessor methods with const-correct overload.
    const t_Class* operator-> () const {return m_InternalPointer;}
    t_Class* operator ->() {return m_InternalPointer;}

    const t_Class* operator& () const {return m_InternalPointer;}
    t_Class* operator& () {return m_InternalPointer;}
    };

    class Dummy;

    class XY
    {
    public:
    ConstCorrectAccessor<Dummy> Me;
    };

    void foo (const XY& xy)
    {
    const Dummy* OK = &xy.Me;
    Dummy* DOES_NOT_COMPILE = &xy.Me;
    }
    Stuart Redmann, Jul 29, 2011
    #12
  13. Am 28.07.2011 15:11, schrieb Stuart Redmann:
    > // Accessor methods with const-correct overload.
    > const t_Class* operator-> () const {return m_InternalPointer;}
    > t_Class* operator ->() {return m_InternalPointer;}
    >
    > const t_Class* operator& () const {return m_InternalPointer;}
    > t_Class* operator& () {return m_InternalPointer;}


    Damn, thats quite an interesting way to solve this, although it took
    me a minute to understand ;-).

    This changes the design etc. of the class, and is a little verbose, so
    I'll stick with the const/non const duplicates as long as the
    functions are small.

    I fear there is not such a beauty solution as I hoped (on the other
    hand, I'm glad that I didn't miss any obvious thing ;-)).

    Thanks for input!
    Markus

    --
    Nur tote Fische schwimmen mit dem Strom.
    Markus Keppeler, Jul 29, 2011
    #13
  14. On 29 Jul., Stuart Redmann wrote:

    [snip my own posting ;-)]

    Please ignore this posting and the posting it is refering to. Somehow
    either Google Groups or my proxy seems to be a bit sluggish as I
    haven't seen my posting until 10 minutes ago.
    Stuart Redmann, Jul 29, 2011
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. slide_o_mix
    Replies:
    0
    Views:
    407
    slide_o_mix
    Oct 15, 2003
  2. Alex
    Replies:
    0
    Views:
    378
  3. David Scarlett
    Replies:
    3
    Views:
    817
    mlimber
    Feb 7, 2006
  4. recover
    Replies:
    2
    Views:
    783
    recover
    Jul 25, 2006
  5. fungus
    Replies:
    13
    Views:
    870
    fungus
    Oct 31, 2008
Loading...

Share This Page