From: Jonathan Campbell on
For teaching purposes, I have a std::vector like class:

template <class T> class Array{
public:
...
T& operator[](unsigned int i) const;

...
private:
unsigned int sz_, cap_;
T* dat_;
};

In the middle of a (teaching) class I began to wonder, aloud :(, why

Array<int> a(10);

a[5] = 33;

did not generate a compiler error; according to a small 'test' program
it does not.

Would it have to be:

const T& operator[](unsigned int i) const;

to generate the expected compiler error?

Scratching my head further, I suppose I should have

const T& operator[](unsigned int i) const;

and

T& operator[](unsigned int i);

as std::vector has, but I'm wondering how the compiler decides which to
call in for eaxample

int x = a[5];

a[5] = 33;

TIA,

Jon C.

--
Jonathan Campbell www.jgcampbell.com BT48, UK.
From: Alf P. Steinbach on
* Jonathan Campbell:
> For teaching purposes, I have a std::vector like class:
>
> template <class T> class Array{
> public:
> ...
> T& operator[](unsigned int i) const;
>
> ...
> private:
> unsigned int sz_, cap_;
> T* dat_;
> };
>
> In the middle of a (teaching) class I began to wonder, aloud :(, why
>
> Array<int> a(10);
>
> a[5] = 33;
>
> did not generate a compiler error; according to a small 'test' program
> it does not.
>
> Would it have to be:
>
> const T& operator[](unsigned int i) const;
>
> to generate the expected compiler error?

Yes.

What matters is the constness of the object that you assign to.

Constness of things used in the expression to produce a reference to that
object, doesn't matter.


> Scratching my head further, I suppose I should have
>
> const T& operator[](unsigned int i) const;
>
> and
>
> T& operator[](unsigned int i);
>
> as std::vector has, but I'm wondering how the compiler decides which to
> call in for eaxample
>
> int x = a[5];

If 'a' is const then it uses the const version, if not, not.


> a[5] = 33;

This is the same, depends only on constness of 'a'.


Cheers & hth.,

- Alf
From: Jonathan Campbell on
Alf P. Steinbach wrote:
> * Jonathan Campbell:
>> For teaching purposes, I have a std::vector like class:
>>
>> template <class T> class Array{
>> public:
>> ...
>> T& operator[](unsigned int i) const;
>>
>> ...
>> private:
>> unsigned int sz_, cap_;
>> T* dat_;
>> };
>>
[...]
>> const T& operator[](unsigned int i) const;
>>
>> and
>>
>> T& operator[](unsigned int i);
>>
>> as std::vector has, but I'm wondering how the compiler decides which
>> to call in for eaxample
>>
>> int x = a[5];
>
> If 'a' is const then it uses the const version, if not, not.
>
>
>> a[5] = 33;
>
> This is the same, depends only on constness of 'a'.
>
>

Thanks, that answers my question and another generated by my running
some tests with print statements in the two versions of operator[].

I was starting to wonder why

Array <int> a(10);

int x = a[5];

called the non-const one.

Best regards,

Jon C.





--
Jonathan Campbell www.jgcampbell.com BT48, UK.
From: Stuart Golodetz on
Jonathan Campbell wrote:
> Alf P. Steinbach wrote:
>> * Jonathan Campbell:
>>> For teaching purposes, I have a std::vector like class:
>>>
>>> template <class T> class Array{
>>> public:
>>> ...
>>> T& operator[](unsigned int i) const;
>>>
>>> ...
>>> private:
>>> unsigned int sz_, cap_;
>>> T* dat_;
>>> };
>>>
> [...]
>>> const T& operator[](unsigned int i) const;
>>>
>>> and
>>>
>>> T& operator[](unsigned int i);
>>>
>>> as std::vector has, but I'm wondering how the compiler decides which
>>> to call in for eaxample
>>>
>>> int x = a[5];
>>
>> If 'a' is const then it uses the const version, if not, not.
>>
>>
>>> a[5] = 33;
>>
>> This is the same, depends only on constness of 'a'.
>>
>>
>
> Thanks, that answers my question and another generated by my running
> some tests with print statements in the two versions of operator[].
>
> I was starting to wonder why
>
> Array <int> a(10);
>
> int x = a[5];
>
> called the non-const one.
>
> Best regards,
>
> Jon C.

A further point worth observing (if you haven't already) is that the
reason you can write

T& operator[](unsigned int i) const
{
return dat_[i];
}

without any problem is that the constness of *this here only affects
dat_, not what it points to. In other words, within the above
operator[], dat_ gets treated as if it were of type T *const, not const
T * (so you can still get a non-const reference to the element). Moral
of the story being that it's an easy one to mess up on.

Cheers,
Stu
From: Jonathan Campbell on
Stuart Golodetz wrote:
> Jonathan Campbell wrote:
>> Alf P. Steinbach wrote:
[...]
>
> A further point worth observing (if you haven't already) is that the
> reason you can write
>
> T& operator[](unsigned int i) const
> {
> return dat_[i];
> }
>
> without any problem is that the constness of *this here only affects
> dat_, not what it points to. In other words, within the above
> operator[], dat_ gets treated as if it were of type T *const, not const
> T * (so you can still get a non-const reference to the element). Moral
> of the story being that it's an easy one to mess up on.
>

Very easy :(

Thanks. If I had been called upon to explain the nature of the version
above, the explanation would have been much less to the point than what
you have given.

Jon C.

--
Jonathan Campbell www.jgcampbell.com BT48, UK.