Are free operator->* overloads evil?

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:

  • LHS: an actor returning MyObj *
  • RHS: address of a member

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;
}

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)