17

auto_ptr (shared_ptr as well) try to make their use as transparent as possible; that is, ideally, you should not be able to tell a difference whether you're using an auto_ptr or a real pointer to an object. Consider:

class MyClass
{
public:
    void foo() {  }
};

MyClass* p = new MyClass;
auto_ptr<MyClass> ap(new MyClassp);

p->foo();       // No notational difference in using real
ap->foo();      // pointers and auto_ptrs

When you try to invoke a member function through a pointer-to-member, there is a difference, as auto_ptr obviously doesn't implement op->*():

void (MyClass::*memfun)() = &MyClass::foo;

(p->*memfun)();         // OK
(ap->*memfun)();        // Error op->*() missing
(ap.get()->*memfun)();  // OK

Why is there no support for op->*() in auto_ptr and how would one implement it (I've experimented for some time, but ultimately gave up).

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
Ralf Holly
  • 221
  • 1
  • 2

3 Answers3

8

As Luther points out its non-trivial to implement - but it is possible.

You have to

  1. use templates so the type of the arguments to operator->* can be deduced
  2. take care of possible qualifiers and multiple function arities using overloads
  3. for member function pointers return a callabe object that is:
    • bound to the instance the smart pointer points to
    • implements an operator() with a signature equivalent to the member function

Ignoring qualifiers for the momement, here is how it could basically look (using C++0x to avoid manual repitition):

// pointer to data member:

template<class T, class D>
D& operator->*(std::auto_ptr<T>& p, D T::*mp) {
    return (*p).*mp;
}

// pointer to member function:

template<class T, class R, class... Args> struct Callable {
    typedef R (T::*MFP)(Args...);
    MFP mfp;
    T& instance;

    Callable(T t, MFP mfp) : instance(t), mfp(mfp) {}

    R operator()(Args... a) {
        return (instance.*mfp)(a...);
    }
};

template<class T, class R, class... Args>
Callable<T, R, Args...>
operator->*(std::auto_ptr<T>& p, R (T::*mfp)(Args...)) {
    return Callable<T, R, Args...>(*p, mfp);
}

But in the end, why bother when we could just use functors that bind member pointers in the first place.

While i can't be sure about it, if you combine the knowledge that

  • the implementation is non-trivial
  • there is an easy alternative that works just as well ((*p).*m)

... its probably usually not implemented due to a bad ratio of the work-needed to the gains resulting from this feature.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
4

implementing ->* would require to solve the perfect forwarding problem:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm

operator->* would have to return a callable object with the same parameter list as the pointer-to-member object, correctly handling const-,volatileness and reference types. And then it would have to employ special magic powers to handle default parameters. This is difficult, error prone, unsolvable and eats too much compile time and since pointer-to-members are a comparably marginally popular feature of C++, they are generally left out of smart pointer implementations.

Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
  • 1
    You can't specify default parameters for function-pointers, so you can strike that, and while difficult its not unsolvable. – Georg Fritzsche Jul 17 '10 at 18:47
  • 1
    Note that [#3](http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm#s3) in the paper doesn't apply here. Through deduction we know what the argument types of the member function are and can use a meta-function to map it to the required type, thus reducing the number of required overloads to `N`. – Georg Fritzsche Jul 17 '10 at 20:57
0

I may be wrong, but I think there's no way to overload operator->* for a pointer-to-member-function. This is because p->*memfun, while valid as part of an expression that treats it as a callable object, isn't a valid expression in its own right, and doesn't have a type. There is therefore no valid type for the operator to return.

The following will work for a pointer-to-member, but trying to use it for a pointer-to-member-function gives an error, "invalid use of non-static member function", with GCC, and an internal compiler error with MSVC.

template <class CLASS, typename MEMBER>
MEMBER& operator->*(std::auto_ptr<CLASS>& p, MEMBER CLASS::*m)
{
    return (*p).*m;
}

EDIT: as Georg's answer points out, you can use boost::bind or similar to create a set of overloads for member functions up to a fixed maximum number of arguments, but there's still no way to overload the operator for all possible member functions.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • That should be `(*p).*m`. Also, the operator gets the member function pointers as its second argument - thus their type is available and can be used to specify the return type. – Georg Fritzsche Jul 17 '10 at 19:14
  • @Georg: thanks, that was a cut-and-paste error, fixed now. It still doesn't compile though, since there's no type to match `MEMBER` if `m` is a member function pointer. – Mike Seymour Jul 17 '10 at 19:25
  • You have to overload with `... operator->*(..., R (Class::*mfp)(ArgTypes...))` for that, see my answer. – Georg Fritzsche Jul 17 '10 at 19:27
  • The number of function parameters is finite - C++03 recommends 256 vs. 1024 for template arguments. So you just need to provide enough overloads (e.g. through Boost.PP / a generator script). While `boost::bind` currently has a low limit, a different implementation could be used. – Georg Fritzsche Jul 17 '10 at 19:50
  • @Georg: agreed; you can solve for all the special cases up to whatever limit you (or the standard) consider to be "enough". My answer still stands though - there is no general solution. – Mike Seymour Jul 17 '10 at 20:12