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?
Which is the correct statement about operator overloading? Explanation: Both arithmetic and non-arithmetic operators can be overloaded. The precedence and associativity of operators remains the same after and before operator overloading. 10.
The class member access operator (->) can be overloaded but it is bit trickier. It is defined to give a class type a "pointer-like" behavior. The operator -> must be a member function.
You cannot overload the following operators: . You cannot overload the preprocessor symbols # and ## . An operator function can be either a nonstatic member function, or a nonmember function with at least one parameter that has class, reference to class, enumeration, or reference to enumeration type.
It allows you to provide an intuitive interface to users of your class, plus makes it possible for templates to work equally well with classes and built-in/intrinsic types. Operator overloading allows C/C++ operators to have user-defined meanings on user-defined types (classes).
The best example I am aware of is Boost.Phoenix, which overloads this operator to implement lazy member access.
For those unfamiliar with Phoenix, it is a supremely nifty library for building actors (or function objects) that look like normal expressions:
( arg1 % 2 == 1 ) // this expression evaluates to an actor (3); // returns true since 3 % 2 == 1 // these actors can also be passed to standard algorithms: std::find_if(c.begin(), c.end(), arg1 % 2 == 1); // returns iterator to the first odd element of c
It achieves the above by overloading operator%
and operator==
. - applied to the actor arg1
these operators return another actor. The range of expressions which can be built in this manner is extreme:
// print each element in c, noting its value relative to 5: std::for_each(c.begin(), c.end(), if_(arg1 > 5) [ cout << arg1 << " > 5\n" ] .else_ [ if_(arg1 == 5) [ cout << arg1 << " == 5\n" ] .else_ [ cout << arg1 << " < 5\n" ] ] );
After you have been using Phoenix for a short while (not that you ever go back) you will try something like this:
typedef std::vector<MyObj> container; container c; //... container::iterator inv = std::find_if(c.begin(), c.end(), arg1.ValidStateBit); std::cout << "A MyObj was invalid: " << inv->Id() << std::endl;
Which will fail, because of course Phoenix's actors do not have a member ValidStateBit
. Phoenix gets around this by overloading operator->*
:
(arg1 ->* &MyObj::ValidStateBit) // evaluates to an actor (validMyObj); // returns true // used in your algorithm: container::iterator inv = std::find_if(c.begin(), c.end(), (arg1 ->* &MyObj::ValidStateBit) );
operator->*
's arguments are:
MyObj *
It returns an actor which evaluates the LHS and looks for the specified member in it. (NB: You really, really want to make sure that arg1
returns MyObj *
- you have not seen a massive template error until you get something wrong in Phoenix. This little program generated 76,738 characters of pain (Boost 1.54, gcc 4.6):
#include <boost/phoenix.hpp> using boost::phoenix::placeholders::arg1; struct C { int m; }; struct D { int n; }; int main() { ( arg1 ->* &D::n ) (new C); return 0; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With