|
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