const и перегрузка operator[]

484
10 августа 2017, 23:53

Вопрос касается перегрузки operator[], а именно различия в создании константной и неконстантной версии. В большинстве случаев это будет выглядеть так:

T& operator[](size_t i);
const T& operator[](size_t i) const;

У меня есть несколько теоретических вопросов. Практически всегда сам вызов operator[] не модифицирует объект, а значит даже неконстантную версию стоило бы объявить как const-метод.

Первый вопрос: правильно ли я понимаю, что мы не объявляем его const-методом только потому, что хотим, чтобы эти методы не конфликтовали, и чтобы при обращении к элементу некоторого константного класса вызывался второй вариант перегруженного оператора?

Второй вопрос: верно ли, что если объект будет неконстантный, для него при вызове operator[] всегда будет вызываться первая перегруженная версия?

Answer 1

"неконстантную версию стоило бы объявить как const-метод"

Вы пробовали? Ведь если ваш объект - константный, как вы сможете вернуть неконстантную ссылку на константный объект?

Грубо -

class Test
{
    int& operator[](int i) const { return data[i]; }
private:
    int data[10];
};

Ведь в этом случае data тоже становится константным.

Если бы это было возможно - тем самым была бы дыра, позволяющая законно изменять константный объект...

Answer 2

Практически всегда сам вызов operator[] не модифицирует объект

Зависит от семантики. Например, std::map, std::unordered_map вообще не имеют const версий этого оператора, по той причине, что вызов с несуществующим ранее ключом приводит к созданию нового элемента в контейнере (явное изменение объекта).

Пара, описанных в вопросе сигнатур (с некоторыми нюансами) используется в стандартной библиотеке для std::basic_string, std::vector, std::deque, std::bitset, std::array, std::valarray. Т.е. константная версия возвращает константную ссылку (кроме bitset, он возвращает bool по значению), а неконстантная, соответственно, обычную ссылку на элемент контейнера. У std::valarray, кстати, есть несколько пар перегрузок с разными типами параметров, но это уже другая история.

Есть и типы, имеющие только константную версию оператора. Это std::basic_string_view, std::reverse_iterator, std::move_iterator и std::match_results. Отсутствие обычной версии обусловлено необходимость доступа только-для-чтения к членам-данным объекта.

Но есть и более необычные ситуации, например, для std::unique_ptr и std::shared_ptr, когда они владеют массивами объектов. Например, в std::unique_ptr объявлена такая сигнатура:

T& operator[](size_t i) const;

Т.е. функция константная (не меняет своих членов-данных), но при этом возвращает неконстантую ссылку на объект, которым управляет указатель, и т.о. этот объект может быть изменен. Иначе бы просто сама концепция "умных" указателей была бы не так полезна.

мы не объявляем его const-методом только потому, что хотим, чтобы эти методы не конфликтовали

В частности, да. Это ограничение механизма перегрузки. Если функция останется константной, то она должна будет принимать уже какой-то другой тип в качестве параметра. Т.к. по возвращаемым типам перегрузка не работает. Но и из-за иной ситуации, уже описанной в другом ответе, когда функция константа, то и все члены-данные объекта this в этой функции константы. Чтобы можно было вернуть что-то неконстантное в этом ситуации, надо возвращать либо копию по значению, либо добавлять косвенности, как это сделано в "умных" указателях. Ведь указатель, который нельзя менять не запрещает менять данные, расположенные по адресу, хранимом в указателе.

верно ли, что если объект будет неконстантный, для него при вызове operator[] всегда будет вызываться первая перегруженная версия?

Верно, но ничто не мешает сделать const_cast приведение, добавляющее const. Можно явно, а можно и через передачу в функцию, принимающую константную ссылку. В обратную же сторону (снятие const) приведение стоит делать с большей осторожностью.

READ ALSO
Проблема при вводе ip из textbox | c++ , winsock

Проблема при вводе ip из textbox | c++ , winsock

При попытке вывести ip, вместо заданного выводятся непонятные цифры

433
Как использовать сопрограммы С++ с Boost.Asio?

Как использовать сопрограммы С++ с Boost.Asio?

Есть прокси-сервер, написанный на асинхронном API BoostAsio - async_* функции и коллбеки

517
Не передаются данные методом POST через ajax

Не передаются данные методом POST через ajax

Решил реализовать на своём сайте поисковикСделал через ajax в js

423
как сделать чтобы header не двигался

как сделать чтобы header не двигался

Изменяю размер окна браузера, шапка двигаетсяКак сделать, чтобы заголовок не перемещался?

382