Boost logo

Boost Users :

From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2004-12-06 11:55:48


David Abrahams wrote:
> Tobias Schwinger wrote:
>
>> David Abrahams wrote:
>>
>>> Tobias Schwinger wrote:
>>>
>>>> I see - thanks for your detailed reply.
>>>>
>>>> When using proxy classes as references the assumption that
>>>> modifications of a temporary object are lost at the end of this very
>>>> object's lifetime does not apply
>>>
>>>
>>>
>>>
>>> It most certainly does apply. There may be some cases in which it
>>> doesn't, but the usual case is that it does.
>>>
>>
>> Writing "proxy class with reference semantics" would have been more
>> precise, I figure.
>
>
> I guess not, really. "Reference semantics" is pretty loosely-defined,
> and it's sort of the job of any proxy reference to have some kind of
> "reference semantics." Could you explain some more?
>

;-) OK - let's say "proxy class returned by-value that behaves like an
lvalue reference". But I believe the last paragraph should have cleared
this up, already.

>>>> - modification of the proxy object actually means changing the
>>>> stored data internally referenced by it.
>>>
>>>
>>> Normally the reason for a proxy is that there isn't any "stored data
>>> internally referenced by it." If there's a persistent lvalue, why
>>> not just return a real reference instead of a proxy?
>>
>>
>> Because I have to change its interface.
>> In my particular case the persistent lvalue is of array type.
>
>
> But why not just change the interface in your dereference function and
> hand back a real reference at that point? If you are not storing a copy
> of the value_type within the proxy, I don't see what the proxy can be
> buying you.
>

I'll put a short problem-domain-specific excursion at the end of this
post which explains what exactly the proxy is buying me.

>>>> And why does operator* give me a mutable (temporary) object then, by
>>>> the way ?
>>>
>>>
>>> It is assumed that if you passed non-const R for the reference type,
>>> you are trying to make a writable iterator, and R will have a
>>> (usually non-const) assignment operator that takes a value_type
>>> argument. If we returned a const object, it would be impossible to
>>> write
>>>
>>> *p = x;
>>>
>>
>> If the expression
>>
>> *p
>>
>> gives me a temporary object with no reference (write-through)
>> semantics it does not make sense to write to this temporary by writing
>>
>> *p = x;
>>
>> according to your above rationale, does it ?
>
>
> That is true. But when the reference type is not the same as the
> value_type, an iterator author has to go out of his way to make that
> assignment compile.
>
>> Same goes for:
>>
>> (*p).mutator(value);
>
>
> And *way* out of his way to make this one compile, since the reference
> type must now have forwarding members for each member of the value_type.
>
>> and
>>
>> p->mutator(value);
>
>
> So now we come to this one. This one is entirely in the hands of the
> library to prevent, since the user doesn't usually determine the return
> type of operator->. It seems like a bad idea to allow it silently.
> The library is supplying an operator-> that stores an object of the
> value_type. There are very few situations where modifications to that
> object can go anywhere useful, so it makes sense to prevent them.

Doesn't the fact that reference and value_type are the same and both are
mutable class types indicate that this should work as well ?

After all, it should be just another notation for

   (*i).mutator(value)

from a user's perspective.

> The
> library is able to make sensible default choices without fear because
> the user can always reimplement any operator within his derived iterator.
>

My workaround to allow it feels somewhat ugly, because I have to
redundantly do the same thing iterator_facade does already (use yet
another proxy to aggregate my temporary reference object and make its
operator-> return a pointer to it - just like operator_arrow_proxy
(given that reference and value_type are the same), except that there is
a non-const operator->, too).

!!! However, it works for me. I don't want to try to make you change
things for what could be a special case !!!

I'm not too sure calling mutators on a proxy class really is that
special, though.

>> ...and I can't imagine there is a user who would seriously complain that
>>
>> pointer ptr ( i.operator->().operator->() );
>>
>> gives an invalid pointer ;-).
>>
>>
>>
>> Am I missing something ?
>
>
> There was a guy who complained just last week on this very mailing list
> that putting an iterator_adaptor over a T const* suddenly produced
> writability in the iterator through operator-> !!
> http://lists.boost.org/MailArchives/boost-users/msg08656.php
>

Yeah, I (partially) read it to ensure I am not asking redundant
questions before starting this thread. I still believe adapting pointers
is a different story.

Excursion: what's the proxy buying me ?

I use a generic, resizable container (similar to std::vector but with
memory layout guarantees) to feed vector data to my graphics board
through some rendering API.
The proxy allows me to see a vector object instead of a reference to an
array of floating type.

Operating on plain arrays in order to perform vector arithmetics is
quite unhandy.
Using a converting constructor from a reference to array type is a
radical and error prone approach and would restrict me to use only one
class for vector arithmetics (there is no way to tell if float[3] should
be a homogenous 2D-vector or a non-homogenous 3D-vector, for example).

That's why I use a vector-reference proxy class that allows me to work
on vectors whose data lives somewhere else.

--
Tobias

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net