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