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

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

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

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

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

Ответы [ 5 ]

130 голосов
/ 09 января 2012

->

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

Если возвращаемое значение является другим объектом типа класса, а не указателем, то последующий поиск члена также обрабатывается функцией 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"
}

->*

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

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

.* и .

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

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

28 голосов
/ 08 января 2012

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

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

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

a->->func();

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

a->func();

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

23 голосов
/ 08 января 2012

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

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

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

9 голосов
/ 08 января 2012

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

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

6 голосов
/ 08 января 2012

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

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

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

...