|
Boost : |
From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2002-12-15 22:33:45
"William E. Kempf" <wekempf_at_[hidden]> escribió en el mensaje
news:1450.192.168.1.1.1039964668.squirrel_at_frodo.kempf-ville.com...
>
> Fernando Cacciola said:
>
> [snip William's comments about the interface with operator T&]
>
I agree with your comments about the interface with operator T&.
I show it in the post, actually, because I wanted to follow again the line
of reasonging I had in the past.
That interface didn't had safe_bool but it was a bad choice anyway.
> > Safe_bool will allow the familiar idioms:
> >
> > if ( opt )
> > if ( !opt )
> > if ( opt == 0 )
> > if ( opt != 0 )
> >
> > The first form, which is probably the most used, comunicates exactly
> > what it means.
> > But the second form, using operator ==, appears to me confusing for
> > value-semantic objects because a comparison against 0 as indication of
> > uninitialization is typical of pointers but not of containers.
>
> Some would consider it confusing, and they'd just avoid using it. Others
> won't, and I see no reason to prevent them from using this.
>
Actually, *I* wouldn't find it that much confusing either.
I was thinking of it from the POV of those who see the pointer-like
interface confusing: I argue that if we want to have 'ref()'
instead of 'opertor*()' because the later makes optional look like
something it isn't; then safe_bool would confuse just the same,
so either we keep safe_bool and the rest of the pointer-like interface
(because safe_bool is itself a pointer-like interface) or we drop both.
> > No container-like object that I know of uses that idiom to indicate
> > emptyness() so I believe it would we wrong to have it in this sort of
> > interface
> >
> > As a conclusion, I agree that it is possible to model optional<>
> > entirely as a 1-element-sequence provided that its
> > interface uses explicit member funcions for both value access and
> > emptyness testing.
>
> I can live with that, but I honestly see no reason to avoid the safe-bool.
>
I don't see reason either, but along the same argument I don't see reason
to drop operator*() and ->()
My point is that safe_bool is a pointer-like interface; you use it to write
code like: if ( p ) or if ( p == 0 ); which is the behaviour of a pointer
(notice that it is not comparing the optional value against 0
but its initialization state); so if this interface makes sense, then
the rest of the pointer-like interface makes sense too.
> > I concluded then, and I still think, that
> > for the possible unexisting value access operation,
> > operator*() and ->() allows for a very familiar and comunicative
> > syntax which expresses _exactly_ the semantic of the operation.
>
> I don't agree. ref() on it's own may not indicate anything, but since the
> type's name is "optional", I think everything is conveyed nicely.
>
Yes, but the name optional is not neccesarily near the variable name.
In fact, it seldom is, AFAICT.
Think about data members, for instance.
> On the
> other hand, operator*() and operator->() are only unambiguous in meaning
> when the type fully models a pointer, which optional can not.
>
I disagree.
It is unambiguous when this _particular part_ of the inteface fully
models a pointer; and in optional<>, this part does.
> > It is not just a minor convenience IMO.
> >
> > And to be honest, the fact that this interface allows optional<>
> > to be replaced by a pointer is not the reason why I adopted it,
> > and having though about droping it, I realized that it is not
> > even the reason why the pointer interface is really good.
> > It is really good because it comunicates _clearly_ and _each time_ you
> > access an optional value that the operation will be undefined unless the
> > initialized condition is met.
> > For dumb programmers like me, this is a very important feature
> > of the interface; and I know this based on experience, since I stop
> > being caught often by uninitialized optionals<> once I adopted it.
>
> Are you sure that you've not just become used to the interface, and thus
> don't find the "mixed metaphor" intuitive?
>
Entirely possible, yes.
> Did you ever use an interface
> with an explicit ref() and actually find it difficult to understand?
>
No I didn't.
> > Anyway, I concede that the pointer interface is really just a visual
> > aid. And of course, smart programmers don't need this aid, and can
> > safely deal with whatever consistent interface you gave them.
> > But I'd like to stress out that this particular aid is significantly
> > important.
>
> I'm not so sure.
>
I see.
... but I'm :-)
so I will hold on to it
> > Anyway, I notice that _only_ operator *() and ->(), which are used to
> > access the value,
> > are the part of the pointer-like interface that I consider really
> > important. operator safe_bool() is just handy and it is consistent with
> > optional<> (a value-based container) even though it allows expressions
> > of the form:
> >
> > if ( opt == 0 )
> >
> > just because it goes along with the pointer-like interface.
> > Because this expression (with the intended meaning) is used with
> > pointers. I think that if the pointer-like interface is dropped, this
> > should be dropped too.
>
> By dropping bool conversion entirely? I think you'll find this
> inconvenient, especially with Mr. Dimov's classic:
>
> if ((opt = foo()) != 0)
>
My point is not to drop the bool conversion; my point is that with
optional<>, this bool conversion tests for initialization state,
not optional value, so if it makes sense to have it, and I think it
does, it makes exactly as much sense to have operators *() and ->().
> > A pro of modeling this concept is that allows optionals<> to be
> > interchanged with true pointers; but I concede that this pro is not
> > really that much important.
> > A con is that it could make the programmer believe it is a pointer.
>
> And it makes relational comparisons either non-intuitive, or difficult to
> use! That's key, I think.
>
Agreed. This is a _big_ problem indeed.
> > Since it is fundamental that optional<> is not mistaken to be a pointer,
> > but rather a value-based container modeling the OptionalValue concept,
> > proper documentation is not enough.
> > It is required that the interface of optional<> that looks like a
> > pointer follow _exactly_ pointer semantics. This way, if it comes to
> > ocurr that a programmer did think that optional<> is a pointer, the code
> > he wrote won't behave abnormally.
>
> But you can't do this with relational operations.
>
Right, I can't do this with relops.
> > A way to measure this potential problem is to consider what would happen
> > if optional<T> were actually be replaced by T*
> > With the current definition of the OptionalValue concept, the code will
> > have _exactly_ the same effect, so the potential danger of mistakenly
> > consider optional<T> as a pointer is conceptual but not practical.
> >
> > However, and very unfortunately, this _requires_ the properly well
> > defined relational operators to be disallowed, because they can
> > effectively create practical problems if optional is mistaken for a
> > pointer and used, for example, to test for aliased equivalence as you do
> > when you compare pointers.
>
> So, just to keep pointer-like operations you're going to make the
> interface difficult to use for many valid use cases?
>
I don't know yet. This is a though call.
Having both well defined relational operators and a pointer like interface
_might_ confuse people, but I don't know at to what extent.
if you see: if ( opt0 == opt1 ), out of context, you expect this to do
_exactly_ 0what it would actually do with a value-based definition
of relops.
The only problem is if you see this _and_ you think opt _is_ a pointer,
so you think it is not comparing optional values.
Therefore, we can just _require_ users to keep in mind what optional
really is and have both relops and a pointer-like interface.
Some people, mostly _me_, argued that this is not a good idea
because code would change meaning if optional<> is replaced
by a pointer.
However, after I realized that not being optional a pointer,
thinking it is would get you in trouble no matter how the interface
tries to prevent it. I'm not so sure anymore that forbiding a
useful-in-itself operation in the name of safety is really worth it.
...BTW, this is what you have been telling me from the beginning,
isn't it :-)
But, if I add deep relops, the problem remains, only that with
a different face: optional is not a pointer even though it looks
like it. If optional is used in generic code _and_ you use
comparisons directly (without *), the semantics will be
inconsistents.
But I can choose to _prevent_ users about this mistake on the
documentation.
This would bring the most useful model if used properly.
Fernando Cacciola
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk