Member Function Adapters
The header functional.hpp includes
improved versions of the full range of member function adapters from
the the C++ Standard Library (§ 20.3.8):
- mem_fun_t
- mem_fun1_t
- const_mem_fun_t
- const_mem_fun1_t
- mem_fun_ref_t
- mem_fun1_ref_t
- const_mem_fun_ref_t
- const_mem_fun1_ref_t
as well as the corresponding overloaded helper functions
The following changes have been made to the adapters as specified
in the Standard:
- The first_argument_type typedef has been
corrected for the const_ family of member
function adapters (see below).
- The argument passed to mem_fun1_t and its
variants is passed using the
call_traits::param_type for the member
function's argument type.
first_argument_type
The standard specifies const_mem_fun1_t, for example, like this:
template <class S, class T, class A> class const_mem_fun1_t
: public binary_function<T*, A, S> {
public:
explicit const_mem_fun1_t(S (T::*p)(A) const);
S operator()(const T* p, A x) const;
};
Note that the first argument to
binary_function is T*
despite the fact that the first argument to operator() is
actually of type const T*.
Does this matter? Well, consider what happens when we write
struct Foo { void bar(int) const; };
const Foo *cp = new Foo;
std::bind1st(std::mem_fun(&Foo::bar), cp);
We have created a const_mem_fun1_t object
which will effectively contain the following
typedef Foo* first_argument_type;
The bind1st will then create a
binder1st object that will use this
typedef as the type of a member which will be
initialised with cp. In other words, we will
need to initialise a Foo* member with a
const Foo* pointer! Clearly this is not
possible, so to implement this your Standard Library vendor will have
had to cast away the constness of cp, probably
within the body of bind1st.
This hack will not suffice with the improved binders in this library, so we have had to
provide corrected versions of the member function adapters as well.
Argument Types
The standard defines mem_fun1_t, for example, like this
(§20.3.8 ¶2):
template <class S, class T, class A> class mem_fun1_t
: public binary_function<T*, A, S> {
public:
explicit mem_fun1_t(S (T::*p)(A));
S operator()(T* p, A x) const;
};
Note that the second argument to operator() is
exactly the same type as the argument to the member function. If this
is a value type, the argument will be passed by value and copied twice.
However, if we were to try and eliminate this inefficiency by
instead declaring the argument as const A&, then
if A were a reference type, we would have a reference to a reference,
which is currently illegal (but see C++
core language issue number 106)
So the way in which we want to declare the second argument for
operator() depends on whether or not the member
function's argument is a reference. If it is a reference, we want to
declare it simply as A; if it is a value we want
to declare it as const A&.
The Boost call_traits class
template contains a param_type typedef, which
uses partial specialisation to make precisely this decision. By
declaring the operator() as
S operator()(T* p, typename call_traits<A>::param_type x) const
we achieve the desired result - we improve efficiency without
generating references to references.
Limitations
The call traits template used to realise some improvements relies
on partial specialisation, so these improvements are only available on
compilers that support that feature. With other compilers, the
argument passed to the member function (in the
mem_fun1_t family) will always be passed by
reference, thus generating the possibility of references to references.
Copyright © 2000 Cadenza New Zealand Ltd. Permission to copy,
use, modify, sell and distribute this document is granted provided
this copyright notice appears in all copies. This document is provided
"as is" without express or implied warranty, and with no claim as to
its suitability for any purpose.
Revised 28 June 2000