Boost logo

Boost :

From: Joel de Guzman (joel_at_[hidden])
Date: 2005-10-14 22:54:50


Joe Gottman wrote:
> "Joel de Guzman" <joel_at_[hidden]> wrote in message
> news:diphko$tba$1_at_sea.gmane.org...
>
>>David Abrahams wrote:
>>
>>>Joel de Guzman <joel_at_[hidden]> writes:
>>>
>>>
>>>
>>>>>Rebinding semantics are there _precisely_ and _only_ to solve a problem
>>>>>posed by nullability.
>>>>
>>>>Sorry, I don't buy that.
>>>
>>>
>>>Why not?
>>
>>I think this is again an artifact of the confusion between the
>>dual interfaces that optional provides. optional has an identity
>>crisis of being a pointer (as the author's original model) and
>>a variant<T, nil> (which I suggested later on). The rebinding
>>behavior that Fernando rationalized was long and winding and
>>confusing. I don't recall anymore his exact rationale. All I
>>know is that when you have to explain your design using so much
>>wordings, then something is wrong, and I don't buy the gobbledegook.
>
>
> Here is an interesting thought experiment. What should std::swap do with
> optional<X &>? In general, swap(x, y) usually has the same effect as the
> following code, except for perhaps a stronger exception guarantee and faster
> performance:
> Foo temp(x); // Step 1. Assume x and y are of type Foo
> x = y; // Step 2
> y = temp; // Step 3
>
> That being the case, what would the following code do?
> int a = 1;
> int b = 2;
> optional<int &> x(a);
> optional<int &> y(b);
> swap(x, y);
>
> Assume that swap is not specialized for optional, and consider two cases.
> Case 1: rebind.
> Step 1 of swap above binds temp to a, step 2 rebinds x to b and step
> 3 rebinds y to a. Thus the end result is that x and y are each rebound to
> what the other one was pointing to before.
>
> Case 2: no rebind.
> In this case, Step 1 above binds temp to a as before. But step 2
> results in *x = *y, so a is set equal to b and both a and b now equal 2.
> Then step 3 results in *y = *temp, so b is set equal to a, but since a and b
> are already equal due to step 1, there results in no change. Thus the end
> result is that the original value in a is lost.
>
> Of course we would specialize swap for optional<T> to call swap(*x, *y) if
> both x and y are initialized, but I think the fact that this is not
> equivalent to the unspecialized version is an argument in favor of the
> rebind semantics.

Interesting point! Now consider this:

     int a = 1;
     int b = 2;
     tuple<int&> x(a);
     tuple<int&> y(b);
     swap(x, y);

What happens after the swap? The same thing happens!

before swap
     a: 1
     b: 2
     x: 1
     y: 2
swap
     a: 2
     b: 2
     x: 2
     y: 2

Yet, does tuple have to change and assume a rebinding behavior
as optional did? Definitely not!

(Aside: Keep in mind that we never touched on the nullability of
optionals. There's no nulled optional at all anywhere in this
thought experiment).

At most, I might be inclined to think that boost::reference_wrapper
does the right swap and rebind behavior (**):

     int a = 1;
     int b = 2;
     reference_wrapper<int> x(a);
     reference_wrapper<int> y(b);

before swap
     a: 1
     b: 2
     x: 1
     y: 2
swap
     a: 1
     b: 2
     x: 2
     y: 1

(**) reference_wrapper rebinds only on explicit copy construction
and assignment from another reference_wrapper. Never from
assignment from a T (its embedded type).

Cheers,

-- 
Joel de Guzman
http://www.boost-consulting.com
http://spirit.sf.net

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk