Я просматривал раздел 13.5 после опровержения понятия, что встроенные операторы не участвуют в разрешении перегрузки и заметили, что нет никакого раздела по operator->*
. Это - просто универсальный бинарный оператор.
Его братья, operator->
, operator*
, и operator[]
, все требуются быть нестатическими функциями членства. Это устраняет определение бесплатной функциональной перегрузки к оператору, наиболее часто используемому для получения ссылки из объекта. Но редкое operator->*
не учтен.
В частности, operator[]
имеет много общих черт. Это является двоичным (они упустили прекрасную возможность сделать это не), и это принимает некоторый контейнер слева и некоторый локатор справа. Его раздел специальных правил, 13.5.5, кажется, не имеет фактического эффекта кроме запретить бесплатные функции. (И то ограничение даже устраняет поддержку коммутативности!)
Так, например, это совершенно законно:
#include
#include
using namespace std;
template< class T >
T &
operator->*( pair &l, bool r )
{ return r? l.second : l.first; }
template< class T >
T & operator->*( bool l, pair &r ) { return r->*l; }
int main() {
pair y( 5, 6 );
y->*(0) = 7;
y->*0->*y = 8; // evaluates to 7->*y = y.second
cerr << y.first << " " << y.second << endl;
}
Легко найти использование, но альтернативный синтаксис имеет тенденцию не быть этим плохо. Например, масштабированные индексы для vector
:
v->*matrix_width[2][5] = x; // ->* not hopelessly out of place
my_indexer<2> m( v, dim ); // my_indexer being the type of (v->*width)
m[2][5] = x; // it is probably more practical to slice just once
Комитет по стандартам забывал предотвращать это, это считали слишком ужасным для беспокойства или является там реальными вариантами использования?
Погуглив немного, я нашел больше случаев, когда люди спрашивают, используется ли operator->*
, чем реальных предложений.
В нескольких местах предлагается T &A::operator->*( T B::* )
. Не уверен, отражает ли это замысел дизайнера или ошибочное впечатление, что T &A::operator->*( T A::* )
является встроенным модулем. Не совсем относится к моему вопросу, но дает представление о глубине, которую я обнаружил в онлайн дискуссиях и литературе.
Там было упоминание о "D&E 11.5.4", что, как я полагаю, является Design and Evolution of C++. Возможно, это содержит подсказку. В противном случае, я просто собираюсь заключить, что это немного бесполезного уродства, которое было пропущено стандартизацией, да и большинством других тоже.
Edit См. ниже вставку цитаты из D&E.
Если говорить количественно, то ->*
- это самый жесткий оператор связывания, который может быть перегружен свободной функцией. Все перегрузки постфиксных выражений и унарных операторов требуют нестатических сигнатур функций-членов. Следующими по старшинству после унарных операторов идут касты в стиле C, которые, можно сказать, соответствуют функциям преобразования (operator type()
), которые также не могут быть свободными функциями. Далее идет ->*
, затем умножение. ->*
могло быть как []
или как %
, они могли пойти по любому пути, и они выбрали путь EEEEEEVIL.
Я согласен с вами в том, что в стандарте есть несогласованность. Он не допускает перегрузки оператора []
функциями, не являющимися членами, и допускает его для оператора -> *
. С моей точки зрения, operator []
относится к массивам, как operator -> *
- к структурам / классам (геттер). Члены массива выбираются с помощью индекса. Члены структуры выбираются с помощью указателей на элементы.
Хуже всего то, что у нас может возникнуть соблазн использовать -> *
вместо operator []
, чтобы получить массив, подобный element
int& operator->*(Array& lhs, int i);
Array a;
a ->* 2 = 10;
. Также возможна другая несогласованность. Мы можем использовать функцию, не являющуюся членом, для перегрузки operator + =
и всех операторов формы @ =
), и мы не можем сделать это для operator =
.
Я действительно не знаю, каково обоснование того, чтобы сделать следующее законным
struct X {
int val;
explicit X(int i) : val(i) {}
};
struct Z {
int val;
explicit Z(int i) : val(i) {}
};
Z& operator+=(Z& lhs, const X& rhs) {
lhs.val+=rhs.val;
return lhs;
}
Z z(2);
X x(3);
z += x;
и запрещающим
Z& operator=(Z& lhs, const X& rhs) {
lhs.val=i;
return lhs;
}
z = x;
Извините, что не отвечаю на ваш вопрос, но добавляю еще больше путаницы.
Стандарт (Рабочий проект 2010-02-16, § 5.5) гласит:
Результатом выражения -> * является lvalue , только если его второй операнд является указателем . члену данных. Если второй операнд является нулевым указателем на значение члена (4.11), поведение не определено.
Вы можете захотеть, чтобы это поведение было четко определенным . Например, проверьте, является ли это нулевым указателем, и обработайте эту ситуацию. Итак, я считаю, что стандарт разрешает перегрузку -> * - правильное решение.