ekinmur
ekinmur

Reputation: 75

why does mem_fn() complain about attempt to use a deleted function?

I have the following snippet :

struct Foo {
    Foo(int num):num_(num){}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};

int main() {
    std::vector<Foo*> vpf{ new Foo(3), new Foo(4), new Foo(5) };

    auto pfa =  std::mem_fn(&Foo::print_add);
    int i = 42;

    //std::for_each(vpf.begin(), vpf.end(), [&i](const auto& val){val -> print_add(i);});
    std::for_each(vpf.begin(), vpf.end(), pfa(&i));

    return 0;
}

Commented code that uses a lambda expression actually works as I expect printing 45, 46, 47. The uncommented code with std::for_each that uses mem_fn causes a compilation error attempt to use a deleted function.

Can someone explain why and how I can properly use mem_fn in this scenario?

Upvotes: 0

Views: 382

Answers (3)

AnT stands with Russia
AnT stands with Russia

Reputation: 320747

I don't see how you could possibly obtain a "attempt to use a deleted function" error from this code. Either the code is inaccurate, or that diagnostic message is not the first one in the list.

The very first error in this code would refer to your pfa(&i) subexpression, which is invalid. Firstly, in order to call pfa you'd have to supply two arguments - of Foo * and int type - while you are supplying just one of int * type. Secondly, in the context of std::for_each you are not supposed to call pfa yourself at all, you are supposed to pass pfa itself to std::for_each.

If you wanted to replace your lambda with something "lambdaless", e.g. using the "classic" std::mem_fn functional object, it would look as follows

auto pfa = std::mem_fn(&Foo::print_add);
int i = 42;
std::for_each(vpf.begin(), vpf.end(), std::bind(pfa, std::placeholders::_1, i));

or, using the now-deprecated C++98 library features

auto pfa = std::mem_fun(&Foo::print_add);
int i = 42;
std::for_each(vpf.begin(), vpf.end(), std::bind2nd(pfa, i));

(Back then it was just... more fun somehow. More fun. Geddit? Ha ha ha... )

P.S. Note that (as @T.C. noted in the comments), there's no need to pre-wrap a member pointer through std::mem_fn, if you are planning to use std::bind on it. The first variant can be rewritten as

int i = 42;
std::for_each(vpf.begin(), vpf.end(), 
  std::bind(&Foo::print_add, std::placeholders::_1, i));

Upvotes: 1

krzaq
krzaq

Reputation: 16431

std::mem_fn creates a functor that takes a reference or pointer to the type as the first parameter, and forwards the rest of its parameters to the actual function.

When passing it to an algorithm, you're not supposed to call it's () operator (you didn't do this for your lambda example). Your problem is that you also want to provide an argument to your function, and this simply isn't possible with just mem_fn. You can do this with std::bind or a lambda, though.

std::for_each(vpf.begin(), vpf.end(), std::bind(pfa, std::placeholders::_1, i));

or

std::for_each(vpf.begin(), vpf.end(), [&i,&pfa](const auto& val){ pfa(val, i); });

That being said, I think it's best to go with the lambda from your example. bind is controvesial, and if you're using a lambda anyway, what's the point of adding mem_fn to it?

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118435

auto pfa =  std::mem_fn(&Foo::print_add);

&Foo::print_add is not a function pointer. It is a class method pointer. You can't invoke it as a function pointer. You have to come up with an instance of the class for this method, and invoke the instance's method via the pointer.

std::for_each(vpf.begin(), vpf.end(), pfa(&i));

You are trying to invoke pfa as if it was a function pointer it is not. First of all, std::for_each passes the item in the sequence it iterates over, for each invocation. That's going to be the class instance you need to use with the method pointer:

std::for_each(vpf.begin(), vpf.end(),
          [&i,&pfa](const auto& val){pfa(val, i);});

The lambda takes its parameter, the value in the sequence std::for_each iterates over, and uses it to invoke the class method, via the method pointer. As expected, the resulting output from this is

45
46
47

Upvotes: 0

Related Questions