#c #c 11 #reference #constants #const-cast
#c #c 11 #ссылка #константы #const-cast
Вопрос:
Почему приведенный ниже метод не getRanks()
компилируется и как я могу это исправить изящно?
Все, что я хочу сделать, это определить метод доступа к члену, который возвращает ссылку на член. Ссылка не const
такова, поскольку я вполне могу изменить то, на что она ссылается позже. Но поскольку метод-член не изменяет объект, я объявляю его const
. Затем компилятор (clang, std= c 11) настаивает на том, что существует «привязка ссылки», которая «отбрасывает квалификаторы». Но я ЖЕ НЕ отбрасываю квалификаторы, не так ли? И если да, то почему:
struct teststruct{
vector<int> ranks;
vector<int>amp; getRanks()const{
return ranks;
}
};
Теперь код компилируется, если я изменяю оператор return, чтобы отбросить const:
return const_cast<vector<int>amp;>(ranks);
Но «ранги» не должны быть const в первую очередь, я не понимаю, зачем мне нужно const_cast константу. Я даже не знаю, безопасно ли это делать.
В любом случае, есть ли очиститель для написания этого метода? Может кто-нибудь объяснить, почему такой простой метод здравого смысла терпит неудачу? Я хочу объявить getRanks()
метод « const
«, чтобы я мог вызывать его из других const
методов.
Комментарии:
1.
teststruct const x; x.getRanks().emplace_back();
-> UB.
Ответ №1:
Идея const
функции-члена заключается в том, что вы должны иметь возможность вызывать их для const
объектов. const
функции не могут изменять объект.
Допустим, у вас есть класс
class A
{
int data;
void foo() const
{
}
};
и для объекта, и для вызова функции:
A const a;
a.foo();
Внутри A::foo
this->data
обрабатывается так, как если бы его тип был int const
, а не int
. Следовательно, вы не можете изменить this->data
A:foo()
.
Переходя к вашему примеру, тип this->ranks
in getRanks()
следует рассматривать как const vector<int>
и не vector<int>
. Поскольку автоматическое преобразование const vector<int>
в vector<int>amp;
не разрешено, компилятор жалуется, когда вы определяете функцию как:
vector<int>amp; getRanks()const{
return ranks;
}
Он не будет жаловаться, если вы определите функцию как:
const vector<int>amp; getRanks()const{
return ranks;
}
поскольку const vector<int>
может быть автоматически преобразован в const vector<int>amp;
.
Ответ №2:
ranks
это const
потому, что заключающий объект ( *this
) есть const
, поэтому вам нужно вернуть ссылку на a std::vector<int> const
.
Если вы хотите разрешить клиенту изменять вектор (и тем самым влиять на элемент), тогда получатель не должен быть const
. Обратите внимание, что средство получения в любом случае глупо, поскольку ranks
оно уже является общедоступным элементом данных.
Комментарии:
1. Если getter не является const , он не может быть вызван из метода const. Но я хочу это сделать, я хочу иногда вызывать его из метода const, иногда из методов non_const.
2. затем @kdog создает два метода получения, один const и один non-const.
3. А-ля
std::vector::operator[]
.
Ответ №3:
Вы возвращаете ссылку на ranks
, которая является членом teststruct
. Это означает, что любой, кто получает эту ссылку, может изменять внутренние компоненты teststruct
объекта. Так const
что это ложь.
Не отбрасывайте const
. Вместо этого решите, хотите ли вы, чтобы функция была const
и возвращала копию ranks
или const
ссылку, или быть не- const
и возвращать изменяемую ссылку. При необходимости вы всегда можете иметь оба варианта.
Комментарии:
1. Но подождите. Я думал, что «const» непосредственно перед точкой с запятой в объявлении метода просто означает, что сам этот метод не изменяет объект, функцией-членом которого он является. Это то, что говорится в книге Страуструпа, если я помню. Я не заявляю, что любое использование возвращаемого с этого момента в будущем не изменит объект. Я просто заявляю, что сама функция этого не сделает. И я хочу вернуть ссылку, которую можно изменить, а не копию! Также безопасна ли идея const_cast для использования?
2. @kdog Ну, это позволило бы методу косвенно вызывать изменение объекта.
const_cast
Идея небезопасна, потому что компилятор может делать предположения, основанные на том факте, что он считает, что он не будет изменен.3. Вместо того, чтобы »
const
это ложь», я бы сказал »const
тип /return нарушает корректность const».4. Нет, компилятор должен выдать ошибку в строке v.push_back(42), а не в строке getRanks() … хотя я начинаю видеть проблему. Почему это так запутанно и нелогично для меня и очевидно для всех остальных 😉
5. @kdog
const
Для функции-члена означает, что объект, для которого вызывается функция, являетсяconst
. Другими словами, указатель this является const . И посколькуthis
является const ,this->anything
такжеconst
. Нет требованияgetRanks
возвращать aconst amp;
, но есть требование возвращать объект с типом возвращаемого типа.
Ответ №4:
Он отбрасывает квалификаторы, потому что вы возвращаете ссылку на ranks
. Любой последующий код может изменять ранги. Пример:
auto v = teststruct.getRanks();
v[1] = 5; //assuming v.size() > 1
Вы можете исправить это, вернув копию:
vector<int> getRanks() const
Или const
ссылка:
const vector<int>amp; getRanks() const
Если вы хотите ranks
изменить даже в const
объекте, вы могли бы сделать это:
mutable vector<int> ranks;
Комментарии:
1. Нет, это не исправляет проблему, потому что я, возможно, захочу позже изменить ссылку, возвращаемую getRanks() . Нужны ли мне два getRank, один const и один not? Я просто хочу объявить, что getRanks() САМ по себе не изменяет объект. Что он может быть вызван в методе const.
2. Нет, я не хочу, чтобы ранги были изменяемыми, я хочу проверку const.
3. Тогда это звучит как две перегрузки
vector<int>amp; getRanks();
, иconst vector<int>amp; getRanks() const;
это то, что вы хотите.