Давайте возьмем этот код в качестве примера
#include <iostream>
using namespace std;
struct valStruct {
double& operator[](int i){return values[i];}; //line 6
double operator[](int i) const {return values[i];}; //line 7
double values[4];
};
int main () {
valStruct vals = {0,1,2,3};
cout << "Value before change" << endl;
for ( int i = 0; i < 3; i++ ) {
cout << "vals[" << i << "] = "<< vals[i] << endl;
}
vals[1] = 2.2; // change 2nd element
cout << "Value after change" << endl;
for ( int i = 0; i < 3; i++ ) {
cout << "vals[" << i << "] = "<< vals.values[i] << endl;
}
return 0;
}
Я понимаю, что строка 6 (см. комментарий в коде) разрешает запись (и чтение!?) значения в индекс в массиве values
, а строка 7 только читает это значение.
Я понимаю необходимость объявления const в строке 7 как предотвращение изменения значения, если оно не предназначено (хотя я не понимаю, как, поскольку существует строка 6), но мой вопрос в том, почему я не могу написать строку как
double& operator[](int i) const {return values[i];}; //line 7
который выдает ошибку: binding reference of type ‘double&’ to ‘const double’ discards qualifiers
.
Это также поднимает вопрос, зачем вообще нужна строка 7, если строка 6 существует и может выполнять как запись, так и чтение.
Обновлено:
Я понимаю идею const func() const [предложено здесь] [1], и я не понимаю, как это отвечает на мой вопрос. Я не понял механизма, объясненного двумя ответами, которые отвечают на мой вопрос.
Теперь я понимаю, что вторая строка нужна для работы с константными объектами моей функции.
Я также понимаю, что когда у меня есть функция func() const, она неявно делает члены константными. Это означает, что возвращаемое значение должно быть постоянным, и поэтому это не работает. ´double& operator[](int i) const { return values[i]; };´ в то время как это делает ´const double& operator[](int i) const { return values[i]; };´ [1]: Зачем дважды использовать ключевое слово const в функции-члене класса С++
попробуй const valStruct vals = {0,1,2,3};
Поскольку перегрузка из строки 7 выбирается при работе с const valStruct
, поэтому values[i]
также является константой и не может быть привязана к double&
.
@user304584 user304584 Дополнительную информацию о функции-члене const
можно найти здесь. Там вы также можете найти утверждение: «объекты класса const могут только явно вызывать функции-члены const», которое поможет вам лучше понять ответ.
why do we need line 7 at all since line 6 exists and can do both writing and reading.
Нам нужна строка 7 для работы с const
объектами. Строка 6 не может использоваться для const
объектов типа valStruct
. Это связано с тем, что объекты класса const
могут только явно вызывать константные функции-члены, а перегруженная operator[]
в строке 6 не помечена как константная функция-член. Поэтому его нельзя использовать с константным объектом типа valStruct
. Таким образом, нам нужна строка 7, которая «помечает» перегруженную operator[]
как константную функцию-член. Дополнительную информацию об этом можно найти здесь.
Теперь, если вы измените тип возвращаемого значения в строке 7 на double&
, проблема в том, что здесь вы перегрузили operator[]
как константная функция-член. Это означает, что члены данных также const
. И поскольку мы не можем привязать "ссылка lvalue на неконстантный объект" к объекту const
, мы получаем указанную ошибку. Например,
const double d = 43.4;
double& ref = d;//here we'll get the same error
Ситуация (почему вы получаете ошибку) аналогична приведенному выше фрагменту.
Чтобы решить (избавиться) от этой ошибки, нам нужно изменить тип возвращаемого значения с double&
на const double&
.
this
относится к const
объекту в const
квалифицированных функциях-членах. Здесь:
double& operator[](int i) const {return values[i];}; //line 7
Вы пытаетесь вернуть ссылку на this->values[i];
. Эта ссылка не может быть неконстантной, когда объект const
.
Обычно предоставляются две перегрузки:
double& operator[](int i) { return values[i]; }; // 1
const double& operator[](int i) const { return values[i]; }; // 2
// ^ returned reference is const
// ^ current object is const
Первый можно вызывать только для непостоянных объектов. Когда объект const
, то вызывается второй:
valStruct a;
double x = a[0]; // calls 1
const valStruct b;
double x = b[0]; // calls 2
Потому что копирование double
не так дорого, вы можете вернуть double
вместо ссылки.
This also raises the question of why do we need line 7
Потому что без константы operator[]
вы бы не смогли вызвать b[]
.
Нам нужна строка 7 для работы с
const
объектами. Строка 6 не может использоваться дляconst
объектов типаvalStruct
.