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->
Is this a complete thought, or am I missing something?
I think I was going to say that if we allowed it to return a non-const pointer when is_same
::value == true, that would handle the cases in question.
Okay. For multi_array specifically, that doesn't work, because the reference type is a subarray (faux references) but the value type is a multi_array.
But do they need to? Why not have
subarray const&
behave like
subarray&
and
const_subarray const&
behave like
const_subarray&
I had thought about this at the time and came to the following conclusion (modulo the fog of time since I made the decision):
From my perspective, it seems that if a template function has a signature such as: template <Container> void my_function(Container& A);
Then that function expects to receive a mutable container type. In the case that a function expects to receive an immutable container, the signature is likely to be:
template <Container> void my_function(Container const& A);
IIRC some templated functions are overloaded as above specifically to handle these two cases.
Yes. Those functions are, in my experience, always used to expose references/pointers that have the right mutability. For example,
template <Container> typename Container::value_type& operator[](Container&);
template <Container> typename Container::value_type const& operator[](Container const&);
I'm a little confused by this example. When would you pass a container to operator[]? Or is that irrelevant to the example you are describing? I am trying to consider functions /that I did not write/ are generic on a container type. The above looks like an implementation detail of a container library to me. Furthermore, wouldn't an operator[]() return reference type? If reference and value_type are not the same type, does what you describe here still apply?
They _never_ have different operational semantics. There are several ways to handle that with const proxies. For example,
const_subarray<T>::value_type
could be the same as
subarray<T>::value_type const
I concluded that if I chose the design you describe above, namely that "subarray const&" behave like "subarray&" and "const_subarray const&" behave like "const_subarray&", then I would likely violate the expectations of a template function. Objects of type subarray , the mutable reference proxy, would be passed to the second function above.
I don't think that's a big deal.
I am presuming that you feel that way because "They _never_ have different operational semantics" right?
Suppose for a moment that a one-dimensional subarray is passed to the above function. Using the above design, A::operator[] would return a mutable reference to an underlying data element, though a const reference to the underlying data might have been expected. That mutable reference could be accidentally mutated (for example, by a similarly overloaded function that mutates in one case and does not in the other).
I don't think so; see above.
Could you elaborate here? Are you again referring to "They _never_ have different operational semantics"
In summary, I chose to have subarrays respect the constness built into the language as well as the constness imposed by the choice of proxy object (const_subarray or subarray).
To the best of my memory, this was my thinking at the time.
Understandable, but perhaps this an example of the insufficient imagination I was alluding to.
Actually I am more worried that my imagination is excessive in certain respects. My concern at the moment is quote I keep referring to above. To be honest I have no example of two such functions having different "operational semantics", by which I assume you mean that the actual text of the two functions is identical, but I can imagine hypothetically that such a case could exist. That's where my imagination may be going too far.
The way I see it, you're asking me to sacrifice the ability to provide const-correctness for convenience, and I'm suggesting that no matter how ugly it may be, there's a way to keep everything safe within the current language.
I seems to me that "keeping everything safe within the current language" relies on the assumption that you make above, that I quote multiple times, and that's a statement not about the language, but about the behavior of programmers, which of course is fine so long as it's true and that no good reason exists for straying from that prescription. The way I see it, the current ban on binding temporaries to const references is a language level prescription of the same sort you describe above: that it protects people from making so-called "mistakes" at the expense of capabilities that are useful to library developers. I hope that the Move Semantics proposal makes this conversation irrelevant in the end and that the iterator library can take advantage of what it adds to the language. Where can I find a most recent copy of that proposal, btw? Since iterator adaptors works just fine today, I'm far more interested in how other aspects of the language will change with respect to these issues. Thanks for the responses. I think that they have somewhat clarified my understanding of the issues. Cheers, ron