views:

123

answers:

4

I am writing (as a self-teaching exercise) a simple STL-Like range. It is an Immutable-Random-Access "container". My range, keeps only the its start element, the the number of elements and the step size(the difference between two consecutive elements):

struct range
{
...
private:
  value_type m_first_element, m_element_count, m_step;
};

Because my range doesn't hold the elements, it calculates the desired element using the following:

// In the standards, the operator[]
// should return a const reference.
// Because Range doesn't store its elements
// internally, we return a copy of the value.
value_type operator[](size_type index)
{
    return m_first_element + m_step*index;
}

As you can see, I am not returning a const reference as the standards say. Now, can I assume that a const reference and a copy of the element are the same in terms of using the non-mutating algorithms in the standard library?

Any advice about the subject is greatly appreciated.


@Steve Jessop: Good point that you mentioned iterators.

Actually, I used sgi as my reference. At the end of that page, it says:

Assuming x and y are iterators from the same range:

Invariants Identity
x == y if and only if &*x == &*y

So, it boils down to the same original question I've asked actually :)

+1  A: 

Items in STL containers are expected to be copied around all the time; think about when a vector has to be reallocated, for example. So, your example is fine, except that it only works with random iterators. But I suspect the latter is probably by design. :-P

Chris Jester-Young
+1  A: 

Do you want your range to be usable in STL algorithms? Wouldn't it be better off with the first and last elements? (Considering the fact that end() is oft required/used, you will have to pre-calculate it for performance.) Or, are you counting on the contiguous elements (which is my second point)?

dirkgently
`end()` can be calculated very simply: `m_first_element + m_step*m_element_count`, so it is really not a problem. I wrote everything neede to make `range` an *Immutable* random-access container except what I've said in the question :)
AraK
That was the second part of my question: Is it ok to impose this criterion on the memory management part of the container?
dirkgently
Sorry, I didn't get what you are saying, could you elaborate more :)
AraK
+1  A: 

The standard algorithms don't really use operator[], they're all defined in terms of iterators unless I've forgotten something significant. Is the plan to re-implement the standard algorithms on top of operator[] for your "ranges", rather than iterators?

Where non-mutating algorithms do use iterators, they're all defined in terms of *it being assignable to whatever it needs to be assignable to, or otherwise valid for some specified operation or function call. I think all or most such ops are fine with a value.

The one thing I can think of, is that you can't pass a value where a non-const reference is expected. Are there any non-mutating algorithms which require a non-const reference? Probably not, provided that any functor parameters etc. have enough const in them.

So sorry, I can't say definitively that there are no odd corners that go wrong, but it sounds basically OK to me. Even if there are any niggles, you may be able to fix them with very slight differences in the requirements between your versions of the algorithms and the standard ones.

Edit: a second thing that could go wrong is taking pointers/references and keeping them too long. As far as I can remember, standard algorithms don't keep pointers or references to elements - the reason for this is that it's containers which guarantee the validity of pointers to elements, iterator types only tell you when the iterator remains valid (for instance a copy of an input iterator doesn't necessarily remain valid when the original is incremented, whereas forward iterators can be copied in this way for multi-pass algorithms). Since the algorithms don't see containers, only iterators, they have no reason that I can think of to be assuming the elements are persistent.

Steve Jessop
+1  A: 

Since you put "container" in "quotes" you can do whatever you want.

STL type things (iterators, various member functions on containers..) return references because references are lvalues, and certain constructs (ie, myvec[i] = otherthing) can compile then. Think of operator[] on std::map. For const references, it's not a value just to avoid the copy, I suppose.

This rule is violated all the time though, when convenient. It's also common to have iterator classes store the current value in a member variable purely for the purpose of returning a reference or const reference (yes, this reference would be invalid if the iterator is advanced).

If you're interested in this sort of stuff you should check out the boost iterator library.

Terry Mahaffey