Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > an "optional const"

Reply
Thread Tools

an "optional const"

 
 
.rhavin grobert
Guest
Posts: n/a
 
      09-15-2008
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...
 
Reply With Quote
 
 
 
 
.rhavin grobert
Guest
Posts: n/a
 
      09-15-2008
On 15 Sep., 16:42, Victor Bazarov <v.Abaza...@comAcast.net> 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;
}
}
 
Reply With Quote
 
 
 
 
Vidar Hasfjord
Guest
Posts: n/a
 
      09-15-2008
On Sep 15, 3:52 pm, ".rhavin grobert" <cl...@yahoo.de> 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
 
Reply With Quote
 
Rolf Magnus
Guest
Posts: n/a
 
      09-15-2008
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.

 
Reply With Quote
 
Vidar Hasfjord
Guest
Posts: n/a
 
      09-15-2008
On Sep 15, 6:40*pm, "Alf P. Steinbach" <al...@start.no> 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



 
Reply With Quote
 
Vidar Hasfjord
Guest
Posts: n/a
 
      09-16-2008
On Sep 15, 8:25*pm, "Alf P. Steinbach" <al...@start.no> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments