why does const not work on pointed objects?

C

Corno

Hi all,

There's probably a good reason why a const object can call non const
functions of the objects where it's member pointers point to.
I just don't see it. For me, that makes the the const keyword a lot less
usable.
Can anybody tell me what that good reason is?

TIA,

Corno
 
M

Mike Wahler

Corno said:
Hi all,

There's probably a good reason why a const object can call

Objects cannot 'call' anything.
Code inside a member function can indeed call other (member --
either same class or other class--- or nonmember) functions,
though. The constness of a different type object whose member
function makes the call doesn't matter at all. That's probably
incomprehensible, I know... :)
non const
functions of the objects where it's member pointers point to.


A nonconst member function object can be called only for nonconst objects.
This 'protects' against inadvertent modification of a const object.

A const member function can be called for either const or nonconst objects.
I just don't see it. For me, that makes the the const keyword a lot less
usable.

I find it quite usable.
Can anybody tell me what that good reason is?

I cannot give a reason for something which isn't true. :)

Perhaps you can demonstrate your questions more clear with some code?
(with appropriate accompanying comments).

-Mike
 
A

Andrey Tarasevich

Corno said:
...
There's probably a good reason why a const object can call non const
functions of the objects where it's member pointers point to.
I just don't see it. For me, that makes the the const keyword a lot less
usable.
Can anybody tell me what that good reason is?
...

Because you are dealing with two completely unrelated objects. Would you
please clarify, why do you think that constness of one object (or of an
access path to one object) should somehow implicate constness of a
completely unrelated other object (or of an access path to other object)?
 
J

John Carson

Corno said:
Hi all,

There's probably a good reason why a const object can call non const
functions of the objects where it's member pointers point to.
I just don't see it. For me, that makes the the const keyword a lot
less usable.
Can anybody tell me what that good reason is?

TIA,

Corno

I suspect that you are confusing two things:

1. Whether a pointer is typed as pointing to a const object.
2. Whether a pointer is itself const.

In case 1, you cannot use the pointer to modify what is pointed to (i.e.,
you cannot change the object at the memory address contained in the
pointer). In case 2, you cannot modify the pointer itself (i.e., you cannot
change it so it points to a different memory address).

There are four possible combinations of pointer const-ness, examples of
which are given below:

// not const at all
A. TestClass * ptr;

// non-const pointer to a const object
B. const TestClass * ptr_to_const;

// const pointer to non-const object
C. TestClass * const const_ptr;

// const pointer to const object
D. const TestClass * const const_ptr_to_const;

Which category a pointer falls into depends on where the const is in
relation to the asterisk. If the const is to the left of the asterisk, that
makes it a pointer to a const object (case 1 above). If the pointer is to
the right of the asterisk, that makes it a const pointer (case 2 above).

The rules regarding these various pointer types are as follows:

Pointers to non-const objects ---- A and C above --- can be used to call
non-const member functions. However, if a object is declared to be of const
type, then a pointer to non-const objects cannot store the object's address,
i.e., an attempt to assign the address of a const object to a pointer to
non-const objects will fail to compile. Thus it is not possible to use
pointers to non-const objects to call non-const member functions on a const
object.

Pointers to const objects --- B and D above --- cannot be used to call
non-const member functions but can store the address of both const and
non-const objects.

Putting these two rules together, we see that there is no pointer that
allows you to call a non-const member function on a const object. This would
seem to be the thing that matters as far as respecting const qualifiers
goes.
 
J

Jonathan Turkanis

Andrey Tarasevich said:
Because you are dealing with two completely unrelated objects. Would you
please clarify, why do you think that constness of one object (or of an
access path to one object) should somehow implicate constness of a
completely unrelated other object (or of an access path to other object)?

There was recently a rather heated argument about this issue on the
boost developers list. One participant seemed to be advocating
'deep-constness' as the desirable default behavior for pointers and
smart pointers.

See http://lists.boost.org/MailArchives/boost/msg57577.php and
following.

It was very bloody -- defintely not suitable for children. The
traditional semantics emerged victorious, at least for now ...

Jonathan
 
C

Corno

Andrey Tarasevich said:
Because you are dealing with two completely unrelated objects.

I do not agree with your statement here. The fact that one object stores a
pointer to the other already means that they are not 'completely unrelated'.
A good example is the pimpl idiom; the implementation is definately related
to the abstraction. However, as long as the implementation is not const (Imp
const * const or Imp const *) it is perfectly legal to declare a const
function in the abstraction that calls a non const function of the
implementation.

Corno
 
J

John Harrison

Corno said:
I do not agree with your statement here. The fact that one object stores a
pointer to the other already means that they are not 'completely unrelated'.
A good example is the pimpl idiom; the implementation is definately related
to the abstraction. However, as long as the implementation is not const (Imp
const * const or Imp const *) it is perfectly legal to declare a const
function in the abstraction that calls a non const function of the
implementation.

Corno

I think the point is that any relationship that exists between two classes
is imposed by you. Since you are writing the code it is your responsibility
to get the relationship right, not the language's. In the case you quote its
not difficult.

One the other hand if the rule you want existed in C++, then it might reduce
the chances of trivial error in the pimpl idiom, but it would also
considerable reduce the flexibility of the language for programs which have
more loosely related objects.

E.g.

class Block
{
public:
void move_to(int x, int y) const
{
world->move_to(this, x, y);
}
private:
BlockWorld * world;
};

class BlockWorld
{
public:
void move_to(Block *, int x, int y);
};

Blocks don't hold their position, a blocks position is not part of its
identity, therefore Block::move_to is quite reasonably a const member
function. However there is a BlockWorld object which maintains a list of
blocks and their positions, obviously BlockWorld::move_to cannot be a const
function.

john
 
C

Corno

I suspect that you are confusing two things:
1. Whether a pointer is typed as pointing to a const object.
2. Whether a pointer is itself const.
--snip--

Putting these two rules together, we see that there is no pointer that
allows you to call a non-const member function on a const object. This would
seem to be the thing that matters as far as respecting const qualifiers
goes.
This explains about the restrictions the standard *does* impose. However, my
argument was not that the standard is too strict, but too loose.
To me it would seem logical if a const object cannot call *any* non const
member functions of pointee objects (except if the pointees are specifically
marked as 'mutable').

Corno
 
C

Corno

I think the point is that any relationship that exists between two classes
is imposed by you. Since you are writing the code it is your responsibility
to get the relationship right, not the language's. In the case you quote its
not difficult.
In that case you could throw out the whole const keyword;
"it's your responsibility to write correct code" ;)
One the other hand if the rule you want existed in C++, then it might reduce
the chances of trivial error in the pimpl idiom, but it would also
considerable reduce the flexibility of the language for programs which have
more loosely related objects.
--snip--

Blocks don't hold their position, a blocks position is not part of its
identity, therefore Block::move_to is quite reasonably a const member
function. However there is a BlockWorld object which maintains a list of
blocks and their positions, obviously BlockWorld::move_to cannot be a const
function.
Like I stated in the other post; this would be a situation where you would
have to use 'mutable'.
IOW the function cannot change anything, except when explicitly stated.

Corno
 
A

Andrey Tarasevich

Corno said:
I do not agree with your statement here. The fact that one object stores a
pointer to the other already means that they are not 'completely unrelated'.
A good example is the pimpl idiom; the implementation is definately related
to the abstraction. However, as long as the implementation is not const (Imp
const * const or Imp const *) it is perfectly legal to declare a const
function in the abstraction that calls a non const function of the
implementation.

At the language level storing inside an object a pointer to another
object is not an idiom, it is nothing more than one of the core language
features. This feature can be used to implement a variety of completely
different idioms. You just named one of them, which follows the concept
of 'aggregation'. Yes, in case of 'aggregation' it is perfectly logical
to propagate the constness from enclosing object to the aggregated
object. However, there are many other idioms that can be implemented
using such a pointer. For example, a concept of 'object reference'
(which is not an 'aggregation') does not imply that the constness should
be propagated.

At core language level C++ tries not to limit your choice of programing
idioms. If you are trying to implement an idiom that needs
const-propagation for pointer-based aggregation - implement this
propagation yourself. For example, when implementing pimpl idiom refrain
from accessing the implementation pointer directly. Provide a pair of
accessor functions and use them instead

class Interface
{
...
private:
Implementation* pimpl;
Implementation* get_impl() { return pimpl; }
const Implementation* get_impl() const { return pimpl; }
};

That way you'll achieve the desired const-propagation effect.
 
D

Dylan Nicholson

Andrey Tarasevich said:
Because you are dealing with two completely unrelated objects. Would you
please clarify, why do you think that constness of one object (or of an
access path to one object) should somehow implicate constness of a
completely unrelated other object (or of an access path to other object)?

I assume the OP is referring to something like the following:

#include <string>

struct foo
{
void bar() const
{
data = "hello"; // not allowed
}
std::string data;
};

struct foo2
{
void bar() const
{
*data = "hello"; // allowed
}
std::string* data;
};

In which case, I assume the OP's point is that the fact that 'data' is
held via a pointer makes it no less part of the object's data, so why
should you get non-const access to it from a const member function?
It basically makes constness useless with something like the pimpl
idiom, for instance. The problem is that, as you say, in some cases
the data member pointer may not point to data that is instrinsically
part of the class, in which case the current rules make sense.
There is, of course, a solution: wrap the pointer up in a class
specifically designed to handle this (*not* std::auto_ptr<>, btw):

struct string_ptr
{
/* constructor/destructor presumably to new/delete ptr */
std::string& operator* () { return *ptr; }
const std::string& operator* () const { return *ptr; }
private:
std::string* ptr;
};

struct foo3
{
void bar() const
{
*data = "hello"; // not allowed
}
void bar()
{
*data = "hello"; // allowed
}
string_ptr data;
};


Dylan
 
C

Corno

If you are trying to implement an idiom that needs
const-propagation for pointer-based aggregation - implement this
propagation yourself. For example, when implementing pimpl idiom refrain
from accessing the implementation pointer directly. Provide a pair of
accessor functions and use them instead

class Interface
{
...
private:
Implementation* pimpl;
Implementation* get_impl() { return pimpl; }
const Implementation* get_impl() const { return pimpl; }
};

That way you'll achieve the desired const-propagation effect.
Interesting approach, thanks!

Corno
 
J

John Harrison

Corno said:
In that case you could throw out the whole const keyword;
"it's your responsibility to write correct code" ;)

Not at all. Which functions are const is visible to the user. The internal
pointers of your class are not. That's the whole point.
Like I stated in the other post; this would be a situation where you would
have to use 'mutable'.
IOW the function cannot change anything, except when explicitly stated.

I see mutable as being used for cached values and the like, not for
expressing the relationship between classes, which is far too complex to be
captured in a single concept like mutable. Just attend some OO design
classes if you don't believe me!

john
 
C

Corno

I assume the OP is referring to something like the following:
#include <string>

struct foo
{
void bar() const
{
data = "hello"; // not allowed
}
std::string data;
};

struct foo2
{
void bar() const
{
*data = "hello"; // allowed
}
std::string* data;
};

In which case, I assume the OP's point is that the fact that 'data' is
held via a pointer makes it no less part of the object's data, so why
should you get non-const access to it from a const member function?

so the value of using const in a public member function of a class (it's
interface) is changed for me.
It's no guarantee to the user that nothing will change; it imposes a IMHO
strange restriction on the implementation; a part of the related data of an
instance, eg, only the direct member data is not allowed to change. But as
the example above shows, the same behaviour of the class can be implemented
in another way (which is not a hack to me) that is not hindered by const.
The only thing it does is that it creates 2 interfaces to the same class, it
makes a distinction between all users into 2 groups; users that have full
access to all the member functions and users that can only use the const
functions (either or not really const).

Corno
 
C

Corno

I see mutable as being used for cached values and the like, not for
expressing the relationship between classes, which is far too complex to be
captured in a single concept like mutable. Just attend some OO design
classes if you don't believe me!
You are correct, 'mutable' is not an elegant 'keyword' for such a situation
However, backtracking on your previous example about the Block not being
mutated when moved;
I would have to disagree. To me, moving the block mutates it as well so the
function should not be const.
For me, it would be more logical if 'const' would guarantee that it would
not change the state of the 'model'.

Corno
 
J

Jeff Schwab

Corno said:
so the value of using const in a public member function of a class (it's
interface) is changed for me.

Then you had it wrong all along. The language never guaranteed that
const objects wouldn't change. The "const" keyword is mostly a hint
from one programmer to another that an object may be treated as though
it will not change. The language does not enforce const-ness.
It's no guarantee to the user that nothing will change; it imposes a IMHO
strange restriction on the implementation; a part of the related data of an
instance, eg, only the direct member data is not allowed to change. But as
the example above shows, the same behaviour of the class can be implemented
in another way (which is not a hack to me) that is not hindered by const.
The only thing it does is that it creates 2 interfaces to the same class,

They're not the same class.
it
makes a distinction between all users into 2 groups; users that have full
access to all the member functions and users that can only use the const
functions (either or not really const).

Nonsense. The functionality you want is easily implemented per Dylan's
excellent suggestion.

If it didn't sound so stupid, I'd pull out one of those platitudes about
"the spirit of the language." ;)
 
C

Corno

Then you had it wrong all along. The language never guaranteed that
const objects wouldn't change. The "const" keyword is mostly a hint
from one programmer to another that an object may be treated as though
it will not change. The language does not enforce const-ness.

Thank you for this information, that makes things a bit more clear!

Corno
 
C

Carl Muller

Corno said:
In that case you could throw out the whole const keyword;
"it's your responsibility to write correct code" ;)

Some of us don't have vast quantities of RAM like PC coders have
though... tables are customarily declared const so they can go into
ROM instead of chewing up precious RAM. Of course this also makes
"mutable" a sore point at runtime :)
 
T

tom_usenet

Then you had it wrong all along. The language never guaranteed that
const objects wouldn't change. The "const" keyword is mostly a hint
from one programmer to another that an object may be treated as though
it will not change. The language does not enforce const-ness.

The language enforces const-correctness for a reasonably logical
definition of that concept (ignoring const_cast and mutable for now).

Tom

C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
 
J

John Harrison

Corno said:
You are correct, 'mutable' is not an elegant 'keyword' for such a situation
However, backtracking on your previous example about the Block not being
mutated when moved;
I would have to disagree. To me, moving the block mutates it as well so the
function should not be const.
For me, it would be more logical if 'const' would guarantee that it would
not change the state of the 'model'.

Corno

So you see const as pertaining to the entire world being modelled not to the
individual object? Its a point of view, but I don't think you're going to
find much agreement.

You're seeing object relationships entirely in terms of what is called
aggregation (by some people, the terminology varies). The fact that one
object has a pointer to another object is just an implementation detail
(like your pimpl example). The pointer and the pointee are really one
conceptual object, living and dieing and being const together.

Nothing wrong with aggregation but object relationships can be more complex
and more subtle.

john
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top