Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Const correctness for pointer data members

Reply
Thread Tools

Const correctness for pointer data members

 
 
quantdev2004@yahoo.co.uk
Guest
Posts: n/a
 
      10-04-2005
Hi all,

I have been deling with this kind of code:

class Foo
{
public:
void NonConstMethod()
{}
};

class Bar
{
public:
// [...] (Class simplified)

void NonLogicallyConstMethod() const
{
m_pBar->NonConstMethod();
}
private:
Foo* const m_pBar;

};

Although this is accepted by the compiler, and therefore const-correct,
I often find myself puzzled as to whether this is actually and
logically const-correct.
It just happens that NonLogicallyConstMethod() can invoke a const
method because of the way Bar is associated to Foo i.e. If I used a
class member variable this would not be allowed. The fact that the
pointer itself is const looks like a workaround (not unlike using
<mutable>).

What am I missing?
Thanks,
Quantdev2004

 
Reply With Quote
 
 
 
 
Bob Hairgrove
Guest
Posts: n/a
 
      10-04-2005
On 4 Oct 2005 07:13:50 -0700, http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:

>Hi all,
>
>I have been deling with this kind of code:
>
>class Foo
>{
>public:
> void NonConstMethod()
> {}
>};
>
>class Bar
>{
>public:
> // [...] (Class simplified)
>
> void NonLogicallyConstMethod() const
> {
> m_pBar->NonConstMethod();
> }
>private:
> Foo* const m_pBar;
>
>};
>
>Although this is accepted by the compiler, and therefore const-correct,
>I often find myself puzzled as to whether this is actually and
>logically const-correct.
>It just happens that NonLogicallyConstMethod() can invoke a const
>method because of the way Bar is associated to Foo i.e. If I used a
>class member variable this would not be allowed. The fact that the
>pointer itself is const looks like a workaround (not unlike using
><mutable>).
>
>What am I missing?
>Thanks,
>Quantdev2004


What you are missing is that the const-ness (or in C++ standard-speak,
the "cv-qualification") of the pointer has absolutely nothing to do
with the const-ness (or "cv-qualification") of the thing(s) it points
to. These are two totally and fundamentally different things.

--
Bob Hairgrove
(E-Mail Removed)
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      10-04-2005
(E-Mail Removed) wrote:
> I have been deling with this kind of code:
>
> class Foo
> {
> public:
> void NonConstMethod()
> {}
> };
>
> class Bar
> {
> public:
> // [...] (Class simplified)
>
> void NonLogicallyConstMethod() const
> {
> m_pBar->NonConstMethod();
> }
> private:
> Foo* const m_pBar;


It's a constant pointer to a mutable (non-constant) Foo.

>
> };
>
> Although this is accepted by the compiler, and therefore const-correct,
> I often find myself puzzled as to whether this is actually and
> logically const-correct.


Unknown. Does 'Bar' *own* his copy of 'Foo', to which 'm_pBar' (BTW, do
you think that the name could be a bit corrected, like 'm_pFoo'?) is
pointing? If it does own it, then you're probably right, the logic is
a bit screwed up. If it does not own it, it probably does not matter.

> It just happens that NonLogicallyConstMethod() can invoke a const
> method


Isn't it actually vice versa: it can invoke a NON-const method?

> because of the way Bar is associated to Foo i.e. If I used a
> class member variable this would not be allowed.


In that case the 'Foo' *object* itself would be const.

> The fact that the
> pointer itself is const looks like a workaround (not unlike using
> <mutable>).
>
> What am I missing?


A good book, maybe? And thinking about what to allow and what not to
allow in your own classes...

V
 
Reply With Quote
 
mlimber
Guest
Posts: n/a
 
      10-04-2005
(E-Mail Removed) wrote:
> Hi all,
>
> I have been deling with this kind of code:
>
> class Foo
> {
> public:
> void NonConstMethod()
> {}
> };
>
> class Bar
> {
> public:
> // [...] (Class simplified)
>
> void NonLogicallyConstMethod() const
> {
> m_pBar->NonConstMethod();
> }
> private:
> Foo* const m_pBar;
>
> };
>
> Although this is accepted by the compiler, and therefore const-correct,
> I often find myself puzzled as to whether this is actually and
> logically const-correct.
> It just happens that NonLogicallyConstMethod() can invoke a const
> method because of the way Bar is associated to Foo i.e. If I used a
> class member variable this would not be allowed. The fact that the
> pointer itself is const looks like a workaround (not unlike using
> <mutable>).
>
> What am I missing?
> Thanks,
> Quantdev2004


You're not missing anything. Constness is shallow. That is, C++ does
not transfer constness from the pointer to the pointee. (In your case,
m_pBar is always const, but if you removed that const, it would become
const in const methods, though the pointee would not).

There have been many discussions on this NG and others about why this
is or is not a violation of const-correctness. I find the behavior
non-intuitive and think constness should be transferred to the pointee
in these cases and overridden when necessary with mutable-like
declaration, but changing the language would break a good deal of
existing code.

To get the behavior you want, you can do something like this:

template <class T>
class DeepPtr
{
public:
DeepPtr( T *const ptr ) : m_ptr( ptr ) { assert(m_ptr); }
DeepPtr( DeepPtr<T>& ptr ) : m_ptr( ptr.m_ptr ) {}

// Standard access
T* operator->() { return m_ptr; }
T& operator*() { return *m_ptr; }
T& operator[]( ui32 n ) { return m_ptr[ n ]; }

// Make pointee const if pointer is const
T const* operator->() const { return m_ptr; }
T const& operator*() const { return *m_ptr; }
T const& operator[]( ui32 n ) const { return m_ptr[ n ]; }

private:
T * const m_ptr;

// Disabled methods
DeepPtr( const DeepPtr& );
DeepPtr& operator=( const DeepPtr& );
};

struct Foo
{
void NonConst() {};
};

struct Bar
{
Bar( Foo* pFoo ) : m_pFoo( pFoo ) {}
void Const() const { m_pFoo->NonConst(); } // Error!
private:
DeepPtr<Foo> m_pFoo;
};

Unfortunately, the disabled copy constructor and assignment operator
mean that such a pointer cannot be used in a standard container.

Cheers! --M

 
Reply With Quote
 
Karl Heinz Buchegger
Guest
Posts: n/a
 
      10-04-2005
(E-Mail Removed) wrote:
>
>
> Although this is accepted by the compiler, and therefore const-correct,
> I often find myself puzzled as to whether this is actually and
> logically const-correct.
> It just happens that NonLogicallyConstMethod() can invoke a const
> method because of the way Bar is associated to Foo i.e. If I used a
> class member variable this would not be allowed. The fact that the
> pointer itself is const looks like a workaround (not unlike using
> <mutable>).
>
> What am I missing?


foo* bar; A pointer. Neither the pointer not what it points to is const
foo* const bar; A pointer. The pointer itself is const. But not the object
the pointer points to. In other words: While it is illegal to
make the pointer point to another object, you can change the object
as often as you want

foo const * bar; A pointer. The pointer is not const, but the object the pointer points
to is. This means, the pointer can be reseated to point to different objects
but it is illegal to attempt to change the objects themselfs.

foo const * const bar; Another pointer. This time the pointer and what it points to is const.
Neither can the pointer be reseated to a different object, nor can the object
itself be changed through this pointer

const foo * const bar; identical to foo const * const bar;


const applies to the thing on its left, with the only exception if const is the leftmost specifier.
Then it applies to the thing on its right.

As for your class. Only the things in your class are 'protected' in a const member function. The
pointer
is a part of your class, but not the object the pointer points to.

--
Karl Heinz Buchegger
(E-Mail Removed)
 
Reply With Quote
 
mlimber
Guest
Posts: n/a
 
      10-04-2005
mlimber wrote:
[snip]
>
> template <class T>
> class DeepPtr
> {
> public:
> DeepPtr( T *const ptr ) : m_ptr( ptr ) { assert(m_ptr); }
> DeepPtr( DeepPtr<T>& ptr ) : m_ptr( ptr.m_ptr ) {}
>
> // Standard access
> T* operator->() { return m_ptr; }
> T& operator*() { return *m_ptr; }
> T& operator[]( ui32 n ) { return m_ptr[ n ]; }
>
> // Make pointee const if pointer is const
> T const* operator->() const { return m_ptr; }
> T const& operator*() const { return *m_ptr; }
> T const& operator[]( ui32 n ) const { return m_ptr[ n ]; }
>
> private:
> T * const m_ptr;
>
> // Disabled methods
> DeepPtr( const DeepPtr& );
> DeepPtr& operator=( const DeepPtr& );
> };
>

[snip]

BTW, I should have included <cassert> and ui32 should be translated to
size_t or unsigned int or whatever.

Cheers! --M

 
Reply With Quote
 
quantdev2004@yahoo.co.uk
Guest
Posts: n/a
 
      10-04-2005
> Unknown. Does 'Bar' *own* his copy of 'Foo', to which 'm_pBar' (BTW, do
> you think that the name could be a bit corrected, like 'm_pFoo'?)


yes, it's m_pFoo;

> is
> pointing? If it does own it, then you're probably right, the logic is
> a bit screwed up. If it does not own it, it probably does not matter.


Ownership it's a good discriminant, I didn't think about it!

> > It just happens that NonLogicallyConstMethod() can invoke a const
> > method

>
> Isn't it actually vice versa: it can invoke a NON-const method?


you're right again, sorry about the typos!

> > because of the way Bar is associated to Foo i.e. If I used a
> > class member variable this would not be allowed.

>
> In that case the 'Foo' *object* itself would be const.


that's what I find strange the fact that a syntactic detail (having the
object on the heap and owned, as opposed to a class member variable) is
giving me the chance to avoid const correctness.


>
> A good book, maybe? And thinking about what to allow and what not to
> allow in your own classes...


As I said I have no problems understanding the syntax and semantics. I
take the point on ownership and lifetime management though.

Thanks for you input,
quantdev2004

 
Reply With Quote
 
mlimber
Guest
Posts: n/a
 
      10-04-2005
Victor Bazarov wrote:
[snip]
> Unknown. Does 'Bar' *own* his copy of 'Foo', to which 'm_pBar' ... is
> pointing? If it does own it, then you're probably right, the logic is
> a bit screwed up. If it does not own it, it probably does not matter.

[snip]

I'd suggest that even if Bar does not *uniquely* own that Foo object,
it generally should still transfer its constness to the pointee.
Consider:

class Foo;

class Bar
{
Bar() : pFoo_( new Foo ) {}
~Bar() { delete pFoo_; }
// ...
private:
Foo* const pFoo;
};

Almost certainly, Bar uniquely owns the pointee of pFoo, and in such a
case, transferring the constness of Bar to pFoo should probably also
transfer it to the pointee of pFoo. (Exceptions to that transferring
rule might include caching mechanisms, semaphores, etc., which are
private and still preserve logical constness but not physical
constness.)

When it comes to shared ownership, I'd suggest the same sort of thing
should generally apply. Consider:

class A;

class B
{
B( A& a ) : a_( a ) {} // Obviously, B is not responsible
// for deleting a; possibly better
// would be to do some ref counting

void Const() const; // should a_ be modifiable here?
// ...

private:
A& a_;
};

With the same sort of exception cases (caching schemes etc.),
const-correctness seems to imply that *a_ should usually become const
inside B::Const() even though there is no unique ownership here.

In short, I think transferring constness to pointee members should be
the default in all cases and should be overridden with a mutable
extension. (Compare std::vector and arrays: the first is fully
const-correct as a member in a class, the second is not.)

Cheers! --M

 
Reply With Quote
 
mlimber
Guest
Posts: n/a
 
      10-04-2005
mlimber wrote:
[snip]
> With the same sort of exception cases (caching schemes etc.),
> const-correctness seems to imply that *a_ should usually become const

[snip]

Correction: *a_ should be simply a_ since it is a reference.

 
Reply With Quote
 
quantdev2004@yahoo.co.uk
Guest
Posts: n/a
 
      10-04-2005
>
> To get the behavior you want, you can do something like this:
>
> [...]


Thanks for your contribution. I will give a go to your DeepPtr class.


>
> Unfortunately, the disabled copy constructor and assignment operator
> mean that such a pointer cannot be used in a standard container.


But you wouldn't be able to use "normal" const pointers either, isn't
it?
quantdev2004

 
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


Similar Threads
Thread Thread Starter Forum Replies Last Post
double pointer issue....const-correctness puzzlecracker C++ 6 03-20-2009 12:44 PM
const correctness - should C++ prefer const member over non-const? fungus C++ 13 10-31-2008 05:33 AM
const vector<A> vs vector<const A> vs const vector<const A> Javier C++ 2 09-04-2007 08:46 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 1 09-06-2006 03:00 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 3 09-06-2006 02:58 PM



Advertisments