Scott Meyers - Implementing Member Access Operator
Scott Meyers - Implementing Member Access Operator
When I wrote More Effective C++ in 1995, one of the topics I examined was smart point-
ers. As a result, I get a fair number of questions about them, and one of the most interesting
recent questions came from Andrei Alexandrescu. He asked, “Shouldn’t a really smart
smart pointer overload operator->*? I’ve never seen it done.” I hadn’t seen it done, either,
so I set out to do it. The result is instructive, I think, and for more than just operator->*;
it also involves insights into interesting and useful applications of templates.
Review of operator->*
If you’re like most programmers, you don’t use operator->* on a regular basis, so before
I explain how to implement this operator for smart pointers, let me take a moment to review
the behavior of the built-in version.
Given a class C, a pointer pmf to a parameterless member function of C, and a pointer pc
to a C object, the expression
(pc->*pmf)(); // invoke the member function *pmf on *pc
invokes the member function pointed to by pmf on the object pointed to by pc. Here’s an
example:
class Wombat { // wombats are cute Australian marsupials
public: // that look something like dogs
int dig(); // return depth dug
int sleep(); // return time slept
};
typedef int (Wombat::*PWMF)(); // PWMF is a pointer to a
// Wombat member function
Wombat *pw = new Wombat;
PWMF pmf = &Wombat::dig; // make pmf point to
// Wombat::dig
(pw->*pmf)(); // same as pw->dig();
pmf = &Wombat::sleep; // make pmf point to
// Wombat::sleep
(pw->*pmf)(); // same as pw->sleep();
As you can see, pointers to member functions behave similarly to pointers to regular func-
tions; the syntax is just a little more complicated. By the way, the parentheses around pc-
>*pmf are necessary, because the compiler would interpret
pc->*pmf(); // error!
as
pc->*(pmf()); // error!
template<typename T>
class SP {
public:
...
template<typename ReturnType>
const PMFC<ReturnType>
operator->*( ReturnType (T::*pmf)() ) const;
...
};
1. For an example of the different varieties of smart pointers that can be imagined (plus
some killer-cool C++), check out Kevin S. Van Horn’s web site, http://www.xmis-
sion.com/~ksvhsoft/code/smart_ptrs.html.
...
protected:
T* ptr;
};
Smart pointers that wish to offer operator->* can then just inherit from SmartPtrBase.2
However, it’s probably best to use private inheritance, because the use of public inheritance
would suggest the need to add a virtual destructor to SmartPtrBase, thus increasing its
size (as well as the size of all derived classes). Private inheritance avoids this size penalty,
though it mandates the use of a using declaration to make the privately inherited operator-
>* templates public:
Loose Ends
After I’d developed this approach to implementing operator->* for smart pointers, I
posted my solution to the Usenet newsgroup comp.lang.c++.moderated to see what I’d
overlooked. It wasn’t long before Esa Pulkkinen made these observations:
There are at least two problems with your approach:
1. You can’t use pointers to data members (though this is easy enough to
solve).
2. You can’t use user-defined pointers-to-members. If someone has over-
loaded operator->* to take objects that act like member pointers, you may
want to support such “smart pointers to members” in your smart pointer
class. Unfortunately, you need traits classes to get the result type of such
overloaded operator->*s.
Smart pointers to members! Yikes! Esa’s right.3 Fortunately, this article is long enough
that I can stop here and leave ways of addressing Esa’s observations in the time-honored
form of exercises for the reader. So I will.
2. This design applies only to smart pointers that contain dumb pointers to do the actual pointing.
This is the most common smart pointer design, but there are alternatives. Such alternative designs
may need to package operator->* functionality in a manner other than that described here.
3. He’s righter than I originally realized. Shortly after writing the draft of this article, one of my con-
sulting clients showed me a problem that was naturally solved by smart pointers to members. I
was surprised, too.
Summary
If your goal is to make your smart pointers as behaviorally compatible with built-in point-
ers as possible, you should support operator->*, just like built-in pointers do. The use of
class and member templates makes it easy to implement such support, and packaging the
implementation in the form of a base class facilitates its reuse by other smart pointer au-
thors.
Acknowledgements
In addition to motivating my interest in operator->* in the first place, Andrei Alexan-
drescu helped me simplify my implementation of PMFC. Andrei also provided insightful
comments on earlier drafts of this paper and the accompanying source code, as did Esa
Pulkkinen and Mark Rodgers. I am greatly indebted to each of them for their considerable
help with this article.
4. See Nathan Myers’ article, “Traits: A New and Useful Template Technique,” originally published
in the June 1995 C++ Report and now available at http://www.cantrip.org/traits.html.
Other than offering a chance to show off our knowledge of traits and when typename must
precede the name of a type in a template, this doesn’t appear to have bought us much, but
don’t be fooled; this greatly reduces the work smart pointer classes must do to implement
operator->*. In fact, Mark Rodgers noted that a single operator->* template can sup-
port all possible member function pointers, regardless of the number of parameters taken
by the member functions and whether the member functions are const. Just replace all the
operator->* templates in SP (or SmartPtrBase) with this: