Перегрузка операторов доступа к элементам ->, .* (C++)

Я понимаю большинство перегрузок операторов, за исключением операторов доступа членов ->, .*, ->* и т.п.

В частности, что передается этим операторным функциям и что должно быть возвращено?

Как работает оператор (например, operator->(...)) знаете, на какого члена ссылаются? Это может знать? Это вообще нужно знать?

Наконец, есть ли какие-то постоянные соображения, которые необходимо учитывать? Например, при перегрузке что-то вроде operator[]Как правило, вам понадобится как постоянная, так и неконстантная версия. Требуются ли операторам членского доступа постоянные и неконстантные версии?

5 ответов

Решение

->

Это единственный действительно сложный. Это должна быть нестатическая функция-член, и она не принимает аргументов. Возвращаемое значение используется для поиска члена.

Если возвращаемое значение является другим объектом типа класса, а не указателем, то последующий поиск члена также обрабатывается operator-> функция. Это называется "поведением детализации". Язык связывает воедино operator-> звонки, пока последний не вернет указатель.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

Этот хитр только в том, что в этом нет ничего особенного. Для не перегруженной версии требуется объект указателя на тип класса слева и объект указателя на тип члена справа. Но когда вы перегружаете его, вы можете принять любые аргументы и вернуть все, что захотите. Это даже не должно быть нестатическим членом.

Другими словами, это просто обычный бинарный оператор, как +, -, а также /, Смотрите также: Свободный оператор ->* перегружает зло?

.* а также .

Они не могут быть перегружены. Уже есть встроенное значение, когда левая часть имеет тип класса. Возможно, было бы немного разумно иметь возможность определять их для указателя на левой стороне, но комитет по разработке языка решил, что это будет более запутанным, чем полезным.

перегрузка ->, ->*, ., а также .* может заполнять только те случаи, когда выражение будет неопределенным, оно никогда не сможет изменить значение выражения, которое будет допустимым без перегрузки.

Оператор -> особенный.

"У него есть дополнительные нетипичные ограничения: он должен возвращать объект (или ссылку на объект), который также имеет оператор разыменования указателя, или он должен возвращать указатель, который может использоваться для выбора того, на что указывает стрелка оператора разыменования указателя." Брюс Экель: Мышление CPP Vol-One: оператор->;;

Дополнительный функционал предоставляется для удобства, поэтому вам не нужно звонить

a->->func();

Вы можете просто сделать:

a->func();

Это отличает оператор -> от других перегрузок операторов.

Вы не можете перегрузить доступ члена . (т.е. вторая часть чего -> делает). Однако вы можете перегрузить унарный оператор разыменования * (т.е. первая часть чего -> делает).

C++ -> Оператор в основном объединение двух шагов, и это ясно, если вы думаете, что x->y эквивалентно (*x).y, C++ позволяет настроить, что делать с (*x) часть, когда x это экземпляр вашего класса.

Семантика для -> перегрузка несколько странная, потому что C++ позволяет вам либо возвращать обычный указатель (который будет использоваться для поиска указанного объекта), либо возвращать экземпляр другого класса, если этот класс также предоставляет -> оператор. Когда во втором случае поиск разыменованного объекта продолжается с этого нового экземпляра.

-> Оператор не знает, на какой элемент указывается, он просто предоставляет объект для выполнения фактического доступа к элементу.

Кроме того, я не вижу причин, по которым вы не можете предоставить постоянную и неконстантную версии.

Когда вы перегружаете оператор ->() (здесь не передаются аргументы), то, что фактически делает компилятор, вызывает -> рекурсивно, пока не вернет фактический указатель на тип. Затем он использует правильный член / метод.

Это полезно, например, для создания класса интеллектуальных указателей, который инкапсулирует фактический указатель. Перегруженный оператор-> вызывается, делает все, что он делает (например, блокировка для безопасности потока), возвращает внутренний указатель, а затем компилятор вызывает -> для этого внутреннего указателя.

Что касается константности - это было дано в комментариях и других ответах (вы можете и должны предоставить оба).

Другие вопросы по тегам