I was perusing section 13.5 after refuting the notion that built-in operators do not participate in overload resolution, and noticed that there is no section on operator->*. It is just a generic binary operator.
Its brethren, operator->, operator*, and operator[], are all required to be non-static member functions. This precludes definition of a free function overload to an operator commonly used to obtain a reference from an object. But the uncommon operator->* is left out.
In particular, operator[] has many similarities. It is binary (they missed a golden opportunity to make it n-ary), and it accepts some kind of container on the left and some kind of locator on the right. Its special-rules section, 13.5.5, doesn’t seem to have any actual effect except to outlaw free functions. (And that restriction even precludes support for commutativity!)
So, for example, this is perfectly legal:
#include <utility>
#include <iostream>
using namespace std;
template< class T >
T &
operator->*( pair<T,T> &l, bool r )
{ return r? l.second : l.first; }
template< class T >
T & operator->*( bool l, pair<T,T> &r ) { return r->*l; }
int main() {
pair<int, int> y( 5, 6 );
y->*(0) = 7;
y->*0->*y = 8; // evaluates to 7->*y = y.second
cerr << y.first << " " << y.second << endl;
}
It’s easy to find uses, but alternative syntax tends not to be that bad. For example, scaled indexes for 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
Did the standards committee forget to prevent this, was it considered too ugly to bother, or are there real-world use cases?
Googling around a bit, I found more instances of people asking whether
operator->*is ever used than actual suggestions.A couple places suggest
T &A::operator->*( T B::* ). Not sure whether this reflects designer’s intent or a misimpression thatT &A::operator->*( T A::* )is a builtin. Not really related to my question, but gives an idea of the depth I found in online discussion & literature.There was a mention of “D&E 11.5.4” which I suppose is Design and Evolution of C++. Perhaps that contains a hint. Otherwise, I’m just gonna conclude it’s a bit of useless ugliness that was overlooked by standardization, and most everyone else too.
Edit See below for a paste of the D&E quote.
To put this quantitatively,
->*is the tightest binding operator that can be overloaded by a free function. All the postfix-expression and unary operators overloads require nonstatic member function signatures. Next precedence after unary operators are C-style casts, which could be said to correspond to conversion functions (operator type()), which also cannot be free functions. Then comes->*, then multiplication.->*could have been like[]or like%, they could have gone either way, and they chose the path of EEEEEEVIL.