std::bind should be replaced by a lambda expression. It's idiomatic, and results in better code. There is almost no reason post C++11 to use std::bind.
Doing so is quite straightforward, capture each bind argument by value in the lambda capture list, and provide auto parameters for each of the placeholders, then call the bound callable using std::invoke(). That will handle the cases of member function pointers, as well as regular functions. Now, this is how to do it mechanically, if you were doing this as part of a manual refactoring, the lambda can be made even clearer.
#include <functional> #include <iostream> void f(int n1, int n2, int n3) { std::cout << n1 << ' ' << n2 << ' ' << n3 << '\n'; } int main() { using namespace std::placeholders; int n = 5; auto f1 = std::bind(f, 2, n, _1); f1(10); // calls f(2, 5, 10); auto l1 = [ p1 = 2, p2 = n ](auto _1) { return std::invoke(f, p1, p2, _1); }; l1(10); // idiomatically auto l1a = [=](auto _1){return f(2, n, _1);}; l1a(10); auto f2 = std::bind(f, 2, std::cref(n), _1); auto l2 = [ p1 = 2, p2 = std::cref(n) ](auto _1) { return std::invoke(f, p1, p2, _1); }; // or auto l2a = [ p1 = 2, &p2 = n ](auto _1) { return std::invoke(f, p1, p2, _1); }; // more idiomatically auto l2b = [&](auto _1){f(2, n, _1);}; n = 7; f2(10); // calls f(2, 7, 10); l2(10); l2a(10); l2b(10); }
2 5 10 2 5 10 2 5 10 2 7 10 2 7 10 2 7 10 2 7 10
std::bind evaluates flattens std::bind sub-expressions, and passes the same placeholder parameters down. A nested bind is evaluated with the given parameters, and the result is passed in to the outer bind. So you can have a bind that does something like g( _1, f(_1)), and when you call it with a parameter, that same value will be passed to both g and f. The function g will receive f(_1) as its second parameter.
Now, you could rewrite the whole thing as a lambda, but auto potentially makes this a little more difficult. The result of std::bind is an unutterable type. They weren't supposed to be naked. However, auto means the expression could be broken down into parts, meaning that the translation from a std::bind expression to a lambda expression is potentially not mechanical. Or, the bind could be part of a template, where the subexpression is a template parameter, which is likely working by accident, rather than design.
In any case, std::bind does not treat its arguments uniformly. It treats a bind expression distinctly differently. At the time, it made some sense. But it makes reasoning about bind expressions difficult.
Don't do this. But it is why formally deprecating std::bind is difficult. They can be replaced, but not purely mechanically.
There isn't a simple translation that works, unlike converting from std::auto_ptr to std::unique_ptr, or putting a space after a string where it now looks like a conversion. And, std::bind isn't broken. It's sub-optimal because of the complicated machinery to support all of the flexibility, where a lambda allows the compiler to do much better. Also, since the type isn't utterable, it often ends up in a std::function, which erases the type, removing optimization options.
#include <functional> #include <iostream> void f(int n1, int n2, int n3) { std::cout << n1 << ' ' << n2 << ' ' << n3 << '\n'; } int g(int n1) { return n1; } int main() { using namespace std::placeholders; auto g1 = std::bind(g, _1); auto f2 = std::bind(f, _1, g1, 4); f2(10); // calls f(10, g(10), 4); // THIS DOES NOT WORK // auto l2 = [p1 = g1, p2 = 4](auto _1) {std::invoke(f, _1, p1, p2);}; // l2(10); // The bind translation needs to be composed: auto l1 = [](auto _1){return g(_1);}; auto l2 = [p1 = l1, p2 = 4](auto _1){f(_1, p1(_1), p2); }; // idiomatically auto l2a = [](auto _1) { return f(_1, g(_1), 4);}; l2(10); l2a(10); }
10 10 4 10 10 4 10 10 4
std::bind could be deprecated in C++Next, and removed as soon as C++(Next++). But that right now is non-trivial in some cases.
clean: -rm example1 -rm example2 example1: example1.cpp clang++ --std=c++1z example1.cpp -o example1 example2: example2.cpp clang++ --std=c++1z example2.cpp -o example2 example3: example3.cpp clang++ --std=c++1z example3.cpp -o example3 2>&1 all: example1 example2
rm example1 rm example2 clang++ --std=c++1z example1.cpp -o example1 clang++ --std=c++1z example2.cpp -o example2