Почему метод const не может возвращать неконстантную ссылку?

#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 возвращать a const 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; это то, что вы хотите.