2

I recently wanted to make use of std.container.Array and proceeded to create a class with a getter member function which returns a value from the Array class. I quickly realised that I was not able to const-qualify my getter, since opIndex is a mutable function.

I tried changing the source code to const-qualify Array.opIndex, and it built fine. However, some unit tests in std.algorithm did not pass, complaining that the return value of Array.opIndex is not an lvalue.

Here is the code for Array.opIndex:

ref T opIndex(size_t i)
{
    version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
    return _data._payload[i];
}

Is there something I'm missing here? Why is it not const-qualified?

1 Answers1

5

There are a number of issues with making the containers const-correct, since const makes it so that they can't change anything in their internals, unlike in C++, where you could make some stuff mutable as long as you made sure that the functions were logically const. IIRC, there are operations that Array does which could theoretically be const but can't be due to how some of its internals work. And it wouldn't surprise me if because of that, the folks who have worked on it didn't make any of it const, even if some of it could be.

As for opIndex, I don't see anything obvious in that implementation which couldn't be const, and the fact that it compiled at all implies that it might work. However, if you do that, you need to overload it rather than simply make that particular overload const, or you won't be able to assign to it - which is presumably what std.algorithm was complaining about it. So, you'd need something like

ref T opIndex(size_t i) {...}
ref const(T) opIndex(size_t i) const {...}

so that it still works to assign to it - e.g. arr[5] = "foo"; - as long as the Array isn't const. However, since many of Array's operations can't be const due to how its implementation works, I don't know how useful it really is to make functions like opIndex const, because you'll be very limited in what you can do with a const Array!T even if every member function that can be const is const.

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • You are correct in that I can't just change the original overload to const, I need to add another one. You are also correct in that it needs to return a "ref const(T)" and not a "ref T". The unit tests still work with this change! Perhaps this const version should be added to the code? – David Eränen Aug 19 '14 at 09:56
  • And just to clarify: I did not want opIndex to be const to be able to use it with a "const Array!T"; instead I wanted to be able to const-qualify my getter member function which is using Array.opIndex as read-only. – David Eränen Aug 19 '14 at 15:06
  • @DavidEränen Well, you're using `const Array!T` if you're using `opIndex` inside of a `const` function of a struct or class that it's a member of, but if all you're trying to do is use `opIndex` rather than using it as `const` in general, it looks like it can easily be made to work. Feel free to open an enhancement request https://issues.dlang.org/ or even to create a pull request if you're feeling brave https://github.com/D-Programming-Language/phobos . It looks like more work probably should be done on making `Array` const-correct though, even if we can't make it completely const-correct. – Jonathan M Davis Aug 19 '14 at 17:20