boost.png (6897 bytes) Home Libraries People FAQ More

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):

as well as the corresponding overloaded helper functions

The following changes have been made to the adapters as specified in the Standard:

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