const/non const member function vs. templates

M

Markus Keppeler

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
 
V

Virchanza

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 said:
pGetMe() );
}
 
V

Victor Bazarov

Just one way of going about this:

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

Dummy *pGetMe()
{

}

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

V
 
M

Markus Keppeler

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
 
B

Balog Pal

Markus Keppeler said:
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.
 
W

Werner

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;
}
 
W

Werner

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.

;-)
 
V

Virchanza

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();
}
 
M

Markus Keppeler

Hi!

Am 26.07.2011 17:03, schrieb Balog Pal:
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
 
B

Balog Pal

Markus Keppeler said:
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.
 
S

Stuart Redmann

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
 
S

Stuart Redmann

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;
}
 
M

Markus Keppeler

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
 
S

Stuart Redmann

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top