Boost logo

Boost :

From: William E. Kempf (wekempf_at_[hidden])
Date: 2002-12-12 17:41:16

Fernando Cacciola said:
> From: "Peter Dimov" <pdimov_at_[hidden]>
>> From: "Fernando Cacciola" <fernando_cacciola_at_[hidden]>
>> > > optional<T> m(t);
>> > >
>> > > > foo(m, m); // comparison inside yields false
>> where foo is
>> void foo(optional<T> const &, optional<T> const &);
>> > > >
>> > Nop... :-)
>> >
>> > it compares true because get_pointer(x)==get_poiner(x) is true for
>> any
> x,
>> > whether initialized or not.
>> True. Had foo been
>> void foo(optional<T>, optional<T>);
>> or had the call been
>> foo(optional<T>(t), optional<T>(t)); // corresponds to foo(&t, &t)
>> I would have been right.
> Ouch! Nasty :-)
> OK, deep-copyness breaks pointer-semantic at some point no matter how
> much you try to fake it....
> But then I have to go back to my original decision of disallowing
> relational operations directly, because they won't work as I wanted
> (with semantics invariant under type change).
> William?

I'm mostly still on the fence. I like the syntactic sugar of being able
to compare optionals under the assumption that "uninitialized" is just
another possible value. I see this being done a lot, and the currently
required verbose "if (opt1 == opt2 && *opt1 == *opt2)" seems to add little
clarity and a lot of inconvenience. However, I also understand how this
can be viewed as confusing and not really compatible with the pointer
semantics present in optional<>.

To be honest, I'm wondering if the pointer semantics should just be
dropped. Here's the rationale you provide for pointer semantics:

* Is easy to test for "uninitialized" state. This is just as easily
covered with the safe-bool idiom.

* Is "safe" on platforms where dereferencing a pointer results in a
non-language exception or a core dump. This is just as easily covered
(and can be made well defined on all platforms) by throwing an exception
when you call a get() that returns a reference.

* Can be dropped in in places where pointers are currently used. I'm not
convinced this reason is compelling enough. The only real use case given
that carries any weight is changing from an optional<foo> to
shared_ptr<foo> if foo grows too complex for efficient copying. But then,
why not simply make foo more "copy friendly" through the letter-envelope
idiom, or maybe give it move semantics? And if that's not viable, is it
that bad to have to deal with refactoring the code in this use case (which
I would think would be relatively rare)?

I see the appeal of pointer semantics, but the issues being raised make me
wonder if the "mixed metaphor" that results is not more trouble than it's
worth. I'm thinking that maybe the interface should be as simple as:

template <typename T>
class optional
   explicit optional(T const& value); // Does it need to be explicit?
   optional(optional const& other);
   template <typename U>
      optional(optional<U> const& other);

   operator safe-bool();
   T& ref();
   T const& ref() const;

template <typename T>
bool operator==(optional<T> const& opt1, optional<T> const& opt2);

template <typename T>
bool operator!=(optional<T> const& opt1, optional<T> const& opt2);

This appears to be much cleaner, and avoids the design issues we've been
struggling with all week. The one wart that may bite users is still the
safe-bool leading to possible confusion when T is bool (or convertable to
bool), but like I've said before, we already have cases in the language
where we have to be vigilant of this, so it doesn't bother me.

William E. Kempf

Boost list run by bdawes at, gregod at, cpdaniel at, john at