Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2001-08-28 08:59:29


On Tuesday 28 August 2001 07:06, you wrote:
> > Personally I think it would be silly to have yet another
> > std::mem_fun replacement with another name. I really hope I can
> > convince you to use call_traits so we can amalgamate the two.
>
> I can't use call_traits; using call_traits changes the semantics of the
> call thanks to that wonderful invention called std::auto_ptr. ;-) The
> interface should not specify optimizations, only behavior.

Binding arguments also changes the semantics of the call. If I have:

typedef some-type T;

struct Foo {
  void bar(T);
};

Foo foo;
T t(some-args);

foo.bar(t); // A
boost::mem_fun(&Foo::bar)(&foo, t); // B
boost::mem_fn(&Foo::bar)(&foo, t); // C

(boost::mem_fun refers to mem_fun from functional.hpp that uses call_traits,
boost::mem_fn refers to the Bind library's version that does not use
call_traits).

The semantics, assuming T is best passed by reference:
  A: bar's T parameter is copy-constructed given the value of t
  B: a reference-to-const T is passed to the mem_fun1_t object. Then that
reference-to-const is used to copy-construct bar's T parameter.
  C: the mf1::operator() function's 2nd parameter gets copy-constructed given
t. Then Foo::bar()'s parameter is copy-constructed given the copy of t.

So the semantics of A is the goal of B or C. In this case, B mimics the
semantics of A because using a reference essentially forwards the name of the
"t" to Foo::bar. C is further from A's semantics because is requires two copy
constructions.

B is not perfect, unfortunately. There are two problems I know of:
  1) It doesn't always make the same number of copies as just calling the
function directly. If implicit conversion is performed on the argument to the
mem_fun1_t object, an additional copy-construction will be added to copy
mem_fun1_t::operator()'s argument into the actual underlying function's
argument. This would occur if, for instance, T != int, but int is implicitly
convertible to T, and we have:
  foo.bar(5); // zero copies, just a conversion
  boost::mem_fun(&Foo::bar)(&foo, 5); // 1 copy
  boost::mem_fn(&Foo::bar)(&foo, 5); // 1 copy

  2) It can make mistakes. If T is an auto_ptr, the line marked B won't even
compile because of auto_ptr's copy constructor.

I chose not to use call_traits in Boost.Function because of #2. In theory, I
would prefer to use call_traits _if_ it could be made safe without requiring
user input.

        Doug


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk