shared_ptr and const

T

Tim H

I understand the semantics of why this works the way it does. But I
wonder if there's a reason for the behaviore at the line marked
"QUESTION". I figured if there is an answer, someone here knows it.

Specifically, part 1 is obvious to most anyone. Part 2 is isomorphic
to part 1, yet behaves differently. If a shared_ptr is const, should
it really allow non-const dereferences?

Thanks,

Tim



#include <boost/shared_ptr.hpp>

int main(void)
{
// part 1
int *pi = new int(1);
const int *cpi = new int(2);

*pi = 11; // ok
*cpi = 22; // compile error

// part 2
boost::shared_ptr<int> spi(new int(3));
const boost::shared_ptr<int> cspi(new int(4));

*spi = 33; // ok
*cspi = 44; // QUESTION: should this be a compile
error?

// part 3
boost::shared_ptr<const int> spci(new int(5));
const boost::shared_ptr<const int> cspci(new int(6));

*spci = 44; // compile error
*cspci = 55; // compile error

return 0;
}
 
P

Pete Becker

Tim said:
// part 2
boost::shared_ptr<int> spi(new int(3));
const boost::shared_ptr<int> cspi(new int(4));

*spi = 33; // ok
*cspi = 44; // QUESTION: should this be a compile
error?

ANSWER: no. cspi is a const pointer to int, not a pointer to const int.
The analogous pointer would be:

int * const cip;

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
D

Dizzy

Tim said:
I understand the semantics of why this works the way it does. But I
wonder if there's a reason for the behaviore at the line marked
"QUESTION". I figured if there is an answer, someone here knows it.

Specifically, part 1 is obvious to most anyone. Part 2 is isomorphic
to part 1, yet behaves differently. If a shared_ptr is const, should
it really allow non-const dereferences?

Thanks,

Tim



#include <boost/shared_ptr.hpp>

int main(void)
{
// part 1
int *pi = new int(1);
const int *cpi = new int(2);

*pi = 11; // ok
*cpi = 22; // compile error

// part 2
boost::shared_ptr<int> spi(new int(3));
const boost::shared_ptr<int> cspi(new int(4));

*spi = 33; // ok
*cspi = 44; // QUESTION: should this be a compile
error?

As someone already replied you confuse "const int*" which is actually
equivalent to "int const*" and shared_ptr<const int> with "int* const"
which is equivalent to "const shared_ptr<int>" (or "shared_ptr<int>
const").

In the first case it's the memory pointed to that is constant. In the second
case it's the pointer itself (or shared_ptr) that is constant.

Generally prefer the form of "T const*" over to "const T*" because:
1. it doesn't create such confusions
2. it will not give you strange errors when dealing with template code and
typedefs, example of such code(quote from C++ Templates Complete Guide):

typedef char* CHARS;
typedef CHARS const CPTR; // constant pointer to chars

The meaning of the second declaration is preserved when we textually replace
CHARS with what it stands for:
typedef char* const CPTR; // constant pointer to chars

However, if we write const before the type it qualifies, this principle
doesn't apply. Indeed, consider the alternative to our first two type
definitions presented earlier:
typedef char* CHARS;
typedef const CHARS CPTR; // constant pointer to chars

Textually replacing CHARS results in a type with a different meaning:
typedef const char* CPTR; // pointer to constant chars

typedefs are textually replaced thus can get into errors when using const
before the const type.
// part 3
boost::shared_ptr<const int> spci(new int(5));
const boost::shared_ptr<const int> cspci(new int(6));

*spci = 44; // compile error
*cspci = 55; // compile error

return 0;
}

shared_ptr<> tries to behave like a normal pointer/reference. And with
pointer/references you have what is called "bitwise constness" as oposed
to "logical constness", ie having a const pointer ("int* const pi = &i")
doesn't restrict the access to the memory pointed to, it just restricts the
access to the pointer itself ("pi = &another;" will error but "*pi = 10"
will not).

In conclusion, if you don't want non-const access to the pointed to object
then make it "shared_ptr<T const>" and not "shared_ptr<T> const". They are
very different...
 
T

Tim H

As someone already replied you confuse "const int*" which is actually
equivalent to "int const*" andshared_ptr<const int> with "int* const"
which is equivalent to "constshared_ptr<int>" (or "shared_ptr<int>
const").
In conclusion, if you don't want non-const access to the pointed to object
then make it "shared_ptr<T const>" and not "shared_ptr<T> const". They are
very different...


I fully apprciate the literal difference. What I am curious about is
parallelism to native pointers.

WHY is it so? Why would it be a bad idea to sub-class shared_ptr and
make "operator->() const" and "operator*() const" return const
references?

The parallel usage is what I want.

const int *cpi = new int;
const shared_ptr<int> shcpi = new int;

*cpi = 2;
*thcpi = 2;

This example is trivial to change, but every piece of code I see does

typedef shared_ptr<int> int_ptr;

So now we always have to have a second typedef for const_int_ptr;

Whipping up a trivial sub-class is easy. So why is it a bad idea?

Thanks,

Tim
 
J

James Kanze

WHY is it so? Why would it be a bad idea to sub-class shared_ptr and
make "operator->() const" and "operator*() const" return const
references?

Because this doesn't mimic the way raw pointers work; it has
counter intuitive semantics.
The parallel usage is what I want.
const int *cpi = new int;
const shared_ptr<int> shcpi = new int;

But those aren't parallel. The parallels would be:
int const* cpi = new int ;
shared_ptr< int const > shcpi = new int ;
or
int* const cpi = new int ;
shared_ptr< int > const shcpi = new int ;

A shared_ptr mimics the semantics of the pointer. And the
const-ness of the pointer is orthogonal to the const-ness of
what is pointed to:
int* p0 ;
int const* p1 ;
int* const p2 ;
int* const* p3 ;
gives:
shared_ptr< int > p0 ;
shared_ptr< int const > p1 ;
shared_ptr< int > const p2 ;
shared_ptr< int const > const p3 ;

More generally said:
*cpi = 2;
*thcpi = 2;
This example is trivial to change, but every piece of code I see does

typedef shared_ptr<int> int_ptr;
So now we always have to have a second typedef for const_int_ptr;

And how is this any different than with a raw pointer?
Whipping up a trivial sub-class is easy. So why is it a bad idea?

Because the results aren't really a "smart pointer", and don't
behave intuitively.
 
T

Tim H

Because this doesn't mimic the way raw pointers work; it has
counter intuitive semantics.

I beg to differ, here.

I can not take all my code that uses "foo *" and "const foo *" and
drop in my typedef'ed foo_ptr. Instead I need to drop in a 2nd
typedef.
And how is this any different than with a raw pointer?

If I have "foo *" and want to qualify it as const, I simply add
"const" before it. If I have a typedef'ed "foo_ptr", and I want to
change it to a const, I add a second typedef and change the typedef I
am using.

It's the lack of being able to globally search and replace "foo *"
with "foo_ptr" that irks me.
Because the results aren't really a "smart pointer", and don't
behave intuitively.

If I say that they behave intuitively for me, is there any caveat or
reason I should not do this for my own code?
 
J

James Kanze

I beg to differ, here.
I can not take all my code that uses "foo *" and "const foo *" and
drop in my typedef'ed foo_ptr. Instead I need to drop in a 2nd
typedef.

In place of what? If you have:
typedef foo* FooPtr ;
you can replace it trivially with:
typedef shared_ptr< foo > FooPtr ;
with exactly the same behavior.

If you have "foo*" and "foo const*", obviously, you'll need two
different shared_ptr. The same as if you had "foo*" and "bar*".
It's more or less the same relationship.
If I have "foo *" and want to qualify it as const, I simply add
"const" before it.

You can, but it's a lot clearer if you add the const after the
foo. Const normally modifies what precedes it.
If I have a typedef'ed "foo_ptr", and I want to
change it to a const, I add a second typedef and change the typedef I
am using.

Exactly the same as with a pointer. If you typedef a pointer,
and what to change it to point to const, you need a second
typedef.
It's the lack of being able to globally search and replace "foo *"
with "foo_ptr" that irks me.
If I say that they behave intuitively for me, is there any caveat or
reason I should not do this for my own code?

Because it confuse anyone who knows C++? Because it violates
the type system? Because it links the const-ness of two
different, unrelated objects?
 
B

BobR

/* """
A shared_ptr mimics the semantics of the pointer. And the
const-ness of the pointer is orthogonal to the const-ness of
what is pointed to:
int* p0 ;
int const* p1 ;
int* const p2 ;
int* const* p3 ;
gives:
shared_ptr< int > p0 ;
shared_ptr< int const > p1 ;
shared_ptr< int > const p2 ;
shared_ptr< int const > const p3 ;

""" */
int * const *p3 ;

The 'int' is read-write, the 'pointer-to-pointer' is read-only, '*p3' is rw:
shared_ptr< int const > const p3 ;

So, this p3 does not look correct to me (i am not familiar with boost).
Seems to me like it might be (?):

shared_ptr< int * const > p3 ;
or:
shared_ptr< int > const *p3 ;
or:
shared_ptr< shared_ptr< int > const > p3 ; // are they 'nestable'
or?

Just because I don't need/use them now doesn't mean that someday I might
desperately need shared_ptr<>. Could you clarify this point?

[ Dang, now I need to re-review Alf's 'Pointer' PDF!! <G>]
 
T

Thomas J. Gritzan

BobR said:
/* """
A shared_ptr mimics the semantics of the pointer. And the
const-ness of the pointer is orthogonal to the const-ness of
what is pointed to:
int* p0 ;
int const* p1 ;
int* const p2 ;
int* const* p3 ;
gives:
shared_ptr< int > p0 ;
shared_ptr< int const > p1 ;
shared_ptr< int > const p2 ;
shared_ptr< int const > const p3 ;

Are you sure about p3?

int const* const p3;
gives:
shared_ptr< int const > const p3 ;
 
B

BobR

Thomas J. Gritzan said:
Are you sure about p3?

int const* const p3;
gives:
shared_ptr< int const > const p3 ;

Hi Thomas,

Are you answering me or James?
( I used makeshift quotes /* """ ... """ */ for James' post.).

I was asking about James':
int * const *p3;

My 'guess' ( assuming 'shared_ptr' is nestable):
shared_ptr< shared_ptr< int > const > p3 ; // are they 'nestable'?

Does that seem correct?

I don't have 'boost', so, I can't 'test' it (I'll wait for TR1 or?).
Thanks.
 
J

James Kanze

A shared_ptr mimics the semantics of the pointer. And the
const-ness of the pointer is orthogonal to the const-ness of
what is pointed to:
int* p0 ;
int const* p1 ;
int* const p2 ;
int* const* p3 ;

This last line should be:

int const* const p3 ;
 
J

James Kanze

/* """
A shared_ptr mimics the semantics of the pointer. And the
const-ness of the pointer is orthogonal to the const-ness of
what is pointed to:
int* p0 ;
int const* p1 ;
int* const p2 ;
int* const* p3 ;
gives:
shared_ptr< int > p0 ;
shared_ptr< int const > p1 ;
shared_ptr< int > const p2 ;
shared_ptr< int const > const p3 ;

""" */
The 'int' is read-write, the 'pointer-to-pointer' is read-only, '*p3' is rw:
So, this p3 does not look correct to me (i am not familiar with boost).

The error is in the raw pointer line, where I duplicated the *,
and not the const, like I meant to. The above is the equivalent
of:

int const* const p3 ;
Seems to me like it might be (?):
shared_ptr< int * const > p3 ;
or:
shared_ptr< int > const *p3 ;
or:
shared_ptr< shared_ptr< int > const > p3 ; // are they 'nestable'
or?

The last. They're nestable. Not that I think that you ever
should nest them. (In practice, I don't use them very often
anyway.)
Just because I don't need/use them now doesn't mean that someday I might
desperately need shared_ptr<>. Could you clarify this point?

If you can't use Boost, Barton and Nackman have an example of a
very similar shared_ptr.
 
T

Thomas J. Gritzan

BobR wrote:
[...]
Hi Thomas,

Are you answering me or James?
( I used makeshift quotes /* """ ... """ */ for James' post.).

I was answering to you, since I didn't notice the quotes (BTW why is
everyone using non-standard quotes?)
But you are right, it should be a reply to James.
I was asking about James':
int * const *p3;

My 'guess' ( assuming 'shared_ptr' is nestable):
shared_ptr< shared_ptr< int > const > p3 ; // are they 'nestable'?

As James already explained else thread, it should be
int const* const p3;

A nested shared_ptr<> rarely would make sense.
 
B

BobR

James Kanze wrote in message...

/* """ // <--- note the quote <G>
The 'int' is read-write, the 'pointer-to-pointer' is read-only, '*p3' is rw:

So, this p3 does not look correct to me (i am not familiar with boost).

The error is in the raw pointer line, where I duplicated the *,
and not the const, like I meant to. The above is the equivalent
of:
int const* const p3 ;
Seems to me like it might be (?): [snip]
or:
shared_ptr< shared_ptr< int > const > p3 ; // are they 'nestable'
or?

The last. They're nestable. Not that I think that you ever
should nest them. (In practice, I don't use them very often
anyway.)
""" */

Yeah, I use them *very* little. That's why I wanted to clarify the issue.
Thank you very much.

/* """
Just because I don't need/use them now doesn't mean that someday I might
desperately need shared_ptr<>. Could you clarify this point?

If you can't use Boost, Barton and Nackman have an example of a
very similar shared_ptr.
""" */

It's not that I can't use 'boost', it's that I don't have it yet. :-}
I prefer to write my own 'clean-up' code for now (learning thing).
I'll probably use 'shared_ptr' when it is incorporated into the standard.

Thanks again, James.
 
B

BobR

Thomas J. Gritzan wrote in message...
BobR wrote:
[...]
Hi Thomas,
Are you answering me or James?
( I used makeshift quotes /* """ ... """ */ for James' post.).

I was answering to you, since I didn't notice the quotes
** (BTW why is everyone using non-standard quotes?) **

Older newsreaders. (I have recently come *up* to OE5!! said:
But you are right, it should be a reply to James.


As James already explained else thread, it should be
int const* const p3;

A nested shared_ptr<> rarely would make sense.

I agree. I just wanted to clarify the syntax (for future).

Thanks Thomas.

[1] - Homey don't play (or pay) ms.
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top