|
Boost : |
From: Thorsten Ottosen (nesotto_at_[hidden])
Date: 2004-01-04 23:28:59
"David B. Held" <dheld_at_[hidden]> wrote in message
news:bt9u41$9c6$1_at_sea.gmane.org...
> "Thorsten Ottosen" <nesotto_at_[hidden]> wrote in message
> news:bt8d39$vah$1_at_sea.gmane.org...
> > [...]
> > The most important application of propagating constness is when
> > storing an object by pointer in a class:
> >
> > class X
> > {
> > smart_ptr<My_polymorhic_type> p_;
> > public:
> > int bar() const
> > {
> > return p_->this_is_also_a_const_function();
> > // compile time error if not
> > }
> > };
> >
> > This is the motivating examlple that I have referred to all the time.
> > I'm sorry I didn't brought it to your attention earlier. This is what I
> > mean by bypassing the type system.
>
> But you're asking the type system to do something that it doesn't
> for real pointers. To do what you want, you can just do this:
>
> int bar() const
> {
> return smart_ptr<My_polymorphic_type const>(p_)->
> this_is_also_a_const_function();
> }
Do you honestly think that is realistic for people to do?
> Maybe it's more verbose than you want, but it's essentially
> what you'd have to do with a raw pointer, no?
It's too trouplesome, so I would just accept that I have no compiler
warnings.
> You're right that
> this is essentially the same as a const_cast<>. But as you also
> point out, you have to a const_cast<> no matter which design is
> chosen. But we all know that increasing constness is always
> safe, whereas decreasing it is often dangerous, and usually
> suspect. So if we must have const_cast<>, why not choose the
> design which forces the safe direction?
AFAICT, one member function i eg. shared_ptr would let us get by without any
cast at all.
> > You and others apparently want to call non-const functions on
> > 'p_'. Fine, although a bit strange.(It is strange because one
> > could not do it if one stored a string member.)
>
> It's not strange at all. Just because an object is const doesn't
> mean that everything it's pointing to must be const.
Certainly not. I'm just saying that it makes sense for it to be const inside
a const member function. It does not
have to be const always. If you allow side-effects in const member
functions, chances are that you would
change the observable state of the object even though your interface
promises not to. That is fraud. And it
can lead to hard to understand behavior.
> Let's go back
> to the parameter example, because it's so common, and thus,
> so important:
>
> void foo(smart_ptr<T> const& p);
>
> Your solution is this:
>
> void foo(T& t);
> foo(*p);
>
> But there's a problem with this "solution". What is that? The
> problem is that you've changed the interface into a form that is
> *not equivalent* to the original. Why do we pass pointers in
> the first place? Is it just for efficiency? Not in C++. That's what
> references are for. No, we pass pointers because *they can
> be null*.
AFAIR, the pointer was not allowed to be 0 inside foo().
> Sure, there is a way to pass a null value in the second
> form, but it requires the use of Boost.Optional, and I hardly think
> that is an ideal "solution" to a problem that only exists by breaking
> existing smart pointer designs.
If you need that, why don't you do it like this:
void foo( T* t );
foo( &*p );
?
> > And the solution would be to make 'p_' mutable. This is what
> > mutable is there for afterall.
>
> But it only solves the problem you introduce. Note that mutable
> does nothing for parameter passing.
See above.
> > [...]
> > Assuming my suggestion, how would you reset the pointer? You
> > can't assign to your argument. You cannot call reset() because it
> > is non-const.
>
> My point is not that your solution does not prevent reset() and such.
> I'm saying that it does not prevent reset() and other non-const
> operations *when the pointee needs to be modified*. So in your
> design, you either get all const or no const. Therefore, if someone
> wants to pass by pointer, they have to do this:
>
> void foo(smart_ptr<T>& p);
>
> Well, that certainly documents that *p can be changed, but it also
> allows p to be changed. That's because protecting p, like so:
>
> void foo(smart_ptr<T> const& p);
>
> would imply that *p is also const, even if it shouldn't be. I already
> explained why you don't want to simply forbid users from using
> this style.
I must have forgot. Isn't foo( T& ); and foo( T* ) enough?
> > You would need in order to call the function either a cast or a
> > new member which returns a mutable pointer, but that should
> > surely be *easy* to provide.
>
> It should be easy but unnecessary. I dare say passing pointers
> as params is as frequent if not more so than using them in const
> member functions.
Ok.
>Making the change you suggest would break
> thousands, possibly millions of lines of code. Is it worth it?
That is not that relevant for the discussion. I would be if a change was
actually made.
> I understand your position better, and sympathize with some
> related points; but I'm not convinced that deep const should be
> the default for all pointers. At best, I think it should be an
> optional policy that you can choose to select for applications
> such as yours, and that's what I will focus on doing for policy_ptr.
Ok, that sounds great.
br
Thorsten
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk