an "optional const"

Discussion in 'C++' started by .rhavin grobert, Sep 15, 2008.

  1. hello;-)

    i frequently need the following construction:

    ReturnParam § Function() §
    {
    /...do something.../
    someType var § = something;
    /...do something.../
    return something;
    }

    where § may be 'const' or nothing. So, if the function is called as a
    member function of a constant object, it needs exactly the same code
    as its non-const sister, but with some additional 'const' somewhere in
    the fuction.

    To reduce code-doubling, it would be very nice to have some "optional
    const", eg. something that the compiler automatically removes if the
    fn is called on an non-const object and translates to 'const' if the
    object is constant.

    As i cant think of any possibility that this would break existing
    code, and furthermore think it should be easy to implement.

    So i'd like to know if someone sees any reason to not propose an
    "optional const", and - just by the way ;-) - where i could to it...
    .rhavin grobert, Sep 15, 2008
    #1
    1. Advertising

  2. On 15 Sep., 16:42, Victor Bazarov <> wrote:
    > .rhavin grobert wrote:
    > > hello;-)

    >
    > > i frequently need the following construction:

    >
    > > ReturnParam § Function() §

    >
    > A 'const' in the return type does not matter.  The difference, however,
    > is sometimes like this:
    >
    >      ReturnType Function() const
    >
    > vs
    >
    >      ReturnType& Function()
    >
    > Therefore, it's not "const" versus "non-const" in the return value type,
    > it's a "temporary object" versus "reference" or
    >
    >      ReturnType const& Function() const
    >
    > vs
    >
    >      ReturnType & Function()
    >
    > , i.e. the 'const' in the return type is not where you show it.
    >
    >
    >
    > > {
    > >   /...do something.../
    > >   someType var § = something;
    > >   /...do something.../
    > >   return something;
    > > }

    >
    > > where § may be 'const' or nothing. So, if the function is called as a
    > > member function of a constant object, it needs exactly the same code
    > > as its non-const sister, but with some additional 'const' somewhere in
    > > the fuction.

    >
    > > To reduce code-doubling, it would be very nice to have some "optional
    > > const", eg. something that the compiler automatically removes if the
    > > fn is called on an non-const object and translates to 'const' if the
    > > object is constant.

    >
    > > As i cant think of any possibility that this would break existing
    > > code, and furthermore think it should be easy to implement.

    >
    > > So i'd like to know if someone sees any reason to not propose an
    > > "optional const",  and - just by the way ;-) - where i could to it...

    >
    > AFAIK, folks who are sure that the 'do something' parts in those two
    > functions do not differ at all, simply write a const version and in the
    > non-const version do the const_cast in and out:
    >
    >      ReturnType const& Function() const
    >      {
    >          /* do something */
    >      }
    >
    >      ReturnType& Function()
    >      {
    >          return const_cast<ReturnType&>(
    >              const_cast<ThisClass const&>(*this).Function()
    >              );
    >      }
    >


    true for trivial functions, i use this macro for this:

    #ifndef INLINE_NC
    #define INLINE_NC(ret,fnc,fnnc,cls)\
    inline ret fnc {return const_cast<ret>(static_cast<const cls
    &>(*this).fnnc);}
    #endif

    but in not-so trivial cases, i often have to double the code.


    struct Something;

    Something* GetSomething()
    {
    SomethingOther* pS = DoSomething();
    if (TestSomething(pS)
    {
    /*.. do something ..*/
    return SomethingOther->Something();
    } else {
    /*.. do something else ..*/
    return NULL;
    }
    }

    Something const* GetSomething() const
    {
    SomethingOther const* pS = DoSomething();
    if (TestSomething(pS)
    {
    /*.. do something ..*/
    return SomethingOther->Something();
    } else {
    /*.. do something else ..*/
    return NULL;
    }
    }
    .rhavin grobert, Sep 15, 2008
    #2
    1. Advertising

  3. On Sep 15, 3:52 pm, ".rhavin grobert" <> wrote:
    [...]
    > but in not-so trivial cases, i often have to double the code.
    >
    > struct Something;
    >
    > Something* GetSomething()
    > {
    > SomethingOther* pS = DoSomething();

    [...]
    > }
    >
    > Something const* GetSomething() const
    > {
    > SomethingOther const* pS = DoSomething();

    [...]

    While your algorithm stays the same for both the const and non-const
    member function you operate on different types; hence you need a
    template. I guess the problem is that DoSomething uses other member
    function that are also overloaded on the constness of the object. The
    solution is to make /this/ an explicit parameter of a static member
    function template:

    Something* GetSomething () {
    return DoGetSomething
    <Something, SomethingOther> (*this);
    }

    const Something* GetSomething () const {
    return DoGetSomething
    <const Something, const SomethingOther> (*this);
    }

    template <class S, class SO, class This>
    static S* DoGetSomething (This& self) {
    SO* p = self.DoSomething ();
    if (self.TestSomething (p)) {
    /*.. do something ..*/
    return p->GetSomething();
    } else {
    /*.. do something else ..*/
    return 0;
    }
    }

    Regards,
    Vidar Hasfjord
    Vidar Hasfjord, Sep 15, 2008
    #3
  4. .rhavin grobert

    Rolf Magnus Guest

    Victor Bazarov wrote:

    > .rhavin grobert wrote:
    >> hello;-)
    >>
    >> i frequently need the following construction:
    >>
    >> ReturnParam § Function() §

    >
    > A 'const' in the return type does not matter. The difference, however,
    > is sometimes like this:
    >
    > ReturnType Function() const
    >
    > vs
    >
    > ReturnType& Function()


    I'd say it's

    const ReturnType& Function() const

    vs

    ReturnType& Function()

    i.e. reference to const versus reference to non-const...

    > ReturnType const& Function() const
    > {
    > /* do something */
    > }
    >
    > ReturnType& Function()
    > {
    > return const_cast<ReturnType&>(
    > const_cast<ThisClass const&>(*this).Function()
    > );
    > }


    .... and here, you write that, too.
    Rolf Magnus, Sep 15, 2008
    #4
  5. On Sep 15, 6:40 pm, "Alf P. Steinbach" <> wrote:
    [...]
    > * Victor Bazarov:
    > > AFAIK, folks who are sure that the 'do something' parts in those two
    > > functions do not differ at all, simply write a const version and in the
    > > non-const version do the const_cast in and out:

    >
    > Effectively, yes, but regarding syntax they try to avoid 'const_cast'.

    [...]
    > One general code reuse scheme goes like this:
    >
    >     private:
    >         ReturnType& fooImpl() const ...
    >
    >     public:
    >         ReturnType& foo() { return fooImpl(); }
    >         ReturnType const& foo() const { return fooImpl(); }


    While this is often an acceptable solution it is not general. If
    fooImpl should need to call other member functions with overload
    resolution based on the constness of the object, then this solution
    wont work. See code below.

    Also, if fooImpl needs to return a reference to a member, as often is
    the case, you still need a const_cast inside the implementation; since
    fooImpl is const while the return value is not. In that case this
    solution is no better than the cast'n'forward idiom.

    The fully general and safe solution is to forward to a template
    function:

    // Const and non-const getter overloads

    #include <iostream>
    using namespace std;

    typedef int X;

    class C {
    X x_;

    public:
    C () {}

    void foo () {cout << "non-const\n";}
    void foo () const {cout << "const\n";}

    X& get () {
    foo ();
    return x_;
    }

    #if 0 // duplicate algorithm
    const X& get () const {
    foo ();
    return x_;
    }
    #else // dangerous cast'n'forward idiom
    const X& get () const {
    return const_cast <C*> (this)->get ();
    }
    #endif

    // safe template solution

    #if 1 // C++98
    X& get2 () {return do_get2 <X> (*this);}
    const X& get2 () const {return do_get2 <const X> (*this);}

    template <class R, class This>
    static R& do_get2 (This& self) {
    self.foo ();
    return self.x_;
    }
    #else // C++0x
    X& get2 () {return do_get2 (*this);}
    const X& get2 () const {return do_get2 (*this);}

    template <class This>
    static auto do_get2 (This& self) -> decltype (self.x_)& {
    self.foo ();
    return self.x_;
    }
    #endif
    };


    void test_getter_overload ()
    {
    cout << "Non-const object tests:\n";
    C c;
    c.get (); // Ok, prints "non-const".
    c.get2 (); // Ok, prints "non-const".

    cout << "Const object tests:\n";
    const C cc;
    cc.get (); // Ouch, prints "non-const".
    cc.get2 (); // Ok, prints "const".
    }

    Regards,
    Vidar Hasfjord
    Vidar Hasfjord, Sep 15, 2008
    #5
  6. On Sep 15, 8:25 pm, "Alf P. Steinbach" <> wrote:
    > * Vidar Hasfjord:
    > [...]
    > >   // safe template solution

    >
    > > #if 1 // C++98
    > >   X& get2 () {return do_get2 <X> (*this);}
    > >   const X& get2 () const {return do_get2 <const X> (*this);}

    >
    > >   template <class R, class This>
    > >   static R& do_get2 (This& self) {
    > >     self.foo ();
    > >     return self.x_;
    > >   }

    > [...]
    > I'm too crossed-eyed to really read the code, but I think it's one of those yes,
    > not "the" but "also one". Credit to some Easter Bloc guy (Russian?). Uh,


    I don't follow/know.

    > correction, I see some "const_cast" above, and no matter how the code works that
    > shouldn't be there if it's the old code, above is *not* the old Russian scheme.


    You misinterpret the example; my code included the cast'n'forward
    solution only for illustration.

    > Anyway, the reason why nobody do such things in practice, but only as academic
    > exercises, is that it's far too much code size overhead for no real gain.


    That's generally true. But for the problem presented by the OP I think
    the template solution is appropriate. His common code may very well
    depend on overload resolution based on object constness. Only a
    template can provide that; unless he duplicates the code.

    Regards,
    Vidar Hasfjord
    Vidar Hasfjord, Sep 16, 2008
    #6
    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.

Share This Page