...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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:
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.
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.
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.
Revised 02 December, 2006
Copyright © 2000 Cadenza New Zealand Ltd.
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)