Document number: N1695=04-0135
Programming Language C++, Evolution Working Group
 
Peter Dimov <pdimov@mmltd.net>
 
September 09, 2004

A Proposal to Make Pointers to Members Callable

Introduction

Many standard library algorithms accept function objects - objects that can appear to the left of a function call operator - as arguments. However, pointers to members do not support the function call syntax and cannot be used directly. Instead, they need to be wrapped with the help of std::mem_fun, std::mem_fun_ref, or tr1::mem_fn.

This paper proposes an extension to the C++ language that will allow pointers to member to appear to the left of the function call operator. This will make C++ more regular and enable the more concise and intuitive std::for_each( v.begin(), v.end(), &Shape::draw ). In addition, the specification of two of the components in TR1, tr1::function and tr1::bind, will be simplified by omitting the special handling of pointers to members. tr1::mem_fn [N1432] will become redundant and its implementation - the trivial identity function; a pointer to member will have the same properties as the function object returned by tr1::mem_fn.

Motivation

Expressing equivalent operations with a uniform syntax is important for generic code. The standard library algorithms, such as for_each, can take advantage of the fact that ordinary function pointers and user-defined function objects support a common function call operator:

struct X
{
    void f() const;
};

void g(X const & x);

struct H
{
    void operator()(X const & x) const;
};

int main()
{
    std::vector<X> v;
    std::for_each( v.begin(), v.end(), g );
    std::for_each( v.begin(), v.end(), H() );
}

However, calling a member function such as &X::f requires a different syntax, and cannot be performed by for_each directly. A pointer to a member function needs to be adapted - wrapped in a function object - first. The standard library supplies mem_fun and mem_fun_ref for this purpose:

int main()
{
    std::vector<X> v;
    std::for_each( v.begin(), v.end(), std::mem_fun_ref(&X::f) );

    std::vector<X*> v;
    std::for_each( v.begin(), v.end(), std::mem_fun(&X::f) );
}

This is unnecessarily verbose. The simple

std::for_each( v.begin(), v.end(), &X::f );

syntax feels more natural and is much easier to teach.

The Library Extensions Technical Report [N1660] contains an enhanced member pointer adapter, tr1::mem_fn [1432], that is more convenient than std::mem_fun and std::mem_fun_ref. Nevertheless, its use is still verbose and can be made redundant by the extension proposed herein, which is modeled on the proven semantics of tr1::mem_fn.

As a reflection of the need for simpler and more accessible syntax, two components of the Technical Report, tr1::function and tr1::bind, treat member pointers exactly as if the proposed extension were already in place. The specification and implementation of these components would be considerably simpler in a language where this is already the case, as the library workarounds implementing the illusion of member pointer callability could be removed.

Proposed Text

Replace the second part of the third sentence in 5.2.2/1 ([expr.call]/1),

or it shall have pointer to function type.

with

or it shall have pointer to function or pointer to member type.

Add a new paragraph after 5.2.2/1 ([expr.call]/1):

In an ordinary function call of the form pm(a1, ..., aN), where pm is of type "pointer to member of T", T shall be a completely defined class type and N shall be 1 when pm is a pointer to data member, M+1 when pm is a pointer to a member function taking M arguments. The behavior of the function call when pm is a pointer to a member function is equivalent to (a1.*pm)(a2, ..., aN), when a1 is of a possibly cv-qualified class type of which T is a base class, ((*a1).*pm)(a2, ..., aN) otherwise. The behavior of the function call when pm is a pointer to a data member is equivalent to a1.*pm when a1 is of a possibly cv-qualified class type of which T is a base class, (*a1).*pm otherwise.

Add

in this context.

to the end of footnote 114.

[Note: changes are relative to ISO/IEC 14882:2003(E).]

Impact on Existing Code

The proposal is a pure extension.

--end