$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
From: David Abrahams (dave_at_[hidden])
Date: 2004-12-13 22:49:03
Ronald Garcia wrote:
> Howdy,
> 
> Dave sent me a forward to this conversation some time ago and I have 
> finally made the time to catch up on the conversation and contribute my 
> two cents.   The issues that Tobias describes regarding operator-> are, 
> I believe, exactly the same issues I encountered while implementing 
> Boost.MultiArray.
> 
> To use an earlier version of iterator adaptors, I had to supply a 
> modified  copy of the library as part of MultiArray's implementation.  
> Using the more recent version of iterator adaptors, I simply had to 
> implement my  own operator->() member function and operator_arrow_proxy 
> object.  Clearly I can implement the behavior I want using the library 
> as-is.
> 
> The question of interest then seems to be the following: Should 
> iterator adaptors somehow more explicitly take my and Tobias' 
> implementation needs into consideration?  Perhaps the library's default 
> behavior when Reference is not a true reference type should be changed; 
>   Perhaps a FAQ entry in the documentation would suffice.  Since I can 
> make my library work correctly today with little pain or code 
> duplication, I'm not particularly passionate about this case.
> 
> With respect to the greater language issue, let me respond to an 
> earlier statement by Dave:
> 
> "If iterator_facade handed you a pointer to non-const value_type, it
> would  allow you to modify this temporary, which would then disappear.
> We don't do that for the same reasons the language won't bind a
> non-const reference to a temporary."
> 
> Around here (the Open Systems Lab at Indiana University), I've been 
> arguing against this property of C++ for a long time, specifically that 
> the language does not allow you to bind temporary to a non-const 
> reference.  I am under the impression that this behavior is meant to 
> "protect"  the programmer from making certain mistakes, but the grave 
> downside to this is that it also prevents a library writer from using 
> proxy objects to mimic reference semantics.
The only case I can think of where that's true is when the reference
type is the same as the value type.  If we allowed operator->
> Taking an example from MultiArray, if I have an 2x2 multidimensional 
> array:
> boost::multi_array<int,2> A(extents[2][2]);
> 
> and a template function that takes some Random Access Sequence:
> 
> template <Sequence>
> process_sequence(Sequence& Seq) { /* ... */ }
> 
> then I cannot pass this function the second "row" of my array using the 
> syntax:
> process_sequence(A[1]);
Why can't you just return a const proxy?
> because the return type of multi_array::operator[]() is an object of 
> type multi_array::subarray.  The whole purpose of the subarray is to 
> behave as though it were a nested array within the multi_array.  Bear 
> in mind that multi_arrays are not implemented using nested arrays:  the 
> internal data is stored as a contiguous heap-allocated array.  You 
> could argue that the entire point of the MultiArray library is to allow 
> one to avoid implementing multi-dimensional arrays  using such a nested 
> form  (i.e.  std::vector<std::vector<int> >).
> 
> In order to make MultiArray work, it must return a proxy object that 
> implements const and nonconst versions of the array operators.  
  class multi_array
  {
      const mutable_proxy operator[](int);
      const immutable_proxy operator[](int);
      ...
  };
Now mutable_proxy only needs one version of the array operators.
Couldn't that work?
> The 
> subarray operators manipulate the data stored in the original 
> multi_array that created the original subarray.
> 
> In fact, the Matrix Template Library (MTL) ran into the same problem. 
> The library often creates a "view" of a matrix as an argument  to a 
> subsequent  function.  But again, if the subsequent  function takes its 
> argument by reference (which it should if the argument is used as an 
> "out parameter"), then the result is a compile-time error.  What was 
> their solution?  I hate to admit this: const_cast.
Did someone think about returning const proxies?
> Let's take a step back for a moment. Why is it okay in the language for 
> me to create a temporary object and immediately call a non-const member 
> function on it, as in:
> A().member_function();
> 
> But it's not okay for me to pass a temporary object to another function 
> that in turn calls that same member function, as in:
> 
> template <class T> call(T& x) { x.member_function(); }
> 
> call(A());
You may note that I'm an author of a major proposal to allow templated
references to bind to non-const rvalues.
> I've been convinced by others that if the move semantics proposal is 
> accepted into the language, it will take care of this issue.
Bingo.
> Perhaps iterator_adaptors should mimic the behavior of C++ as it stands 
> for the purpose of consistency, but I'm not convinced that this 
> property of the language does much for safety. It is clear to me, 
> however, that it hinders library developers who hope to mimic reference 
> semantics with proxies.  
I'm not sure; have they used enough imagination?
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com