Search code examples
c++stdbind

A problem that using std::bind to bind a function


I cannot compile the following c++ code, the error shows inside function call2, I suppose call and call2 are the same but error occurs on call2. I paste the error information to the related code line. Can anyone help me and explain what exactly the signature of the f is after executing the std::bind? Also I provide call3 which is using beast::bind_front_handler to bind the function and it is compiled without error.

BTW: My compiler is clang with c++ standard 14. Thanks for your time.

#include <boost/beast.hpp>
#include <iostream>
#include <string>

namespace beast = boost::beast;

void subfunc(int a) { std::cout << "a=" << a << std::endl; }

void func(int n, std::string s, std::function<void(int)> &&subf) { subf(n); }

template <typename F>
void call(F &&f2) {
  auto f = std::bind(func, 1, "100", std::forward<F>(f2));
  f();
}

void call2(std::function<void(int)> &&f2) {
  auto f = std::bind(func, 1, "100", std::move(f2));
  f(); // No matching function for call to object of type 'std::__1::__bind<void (&)(int, std::__1::basic_string<char>, std::__1::function<void (int)> &&), int, char const (&)[4], std::__1::function<void (int)> >'
}

void call3(std::function<void(int)> &&f2) {
  auto f = beast::bind_front_handler(func, 1, "100", std::move(f2));
  f();
}

int main() {
  call(subfunc);
  call2(subfunc);
  call3(subfunc);
  return 0;
}

Solution

  • Lvalue cannot be bound to rvalue reference.

    When you have:

    void bar(int&&) {}
    int i = 0;
    bar(i);                // error
    

    the last line will not compile.

    This is the reason your code fails in second case.


    std::bind takes all passed arguments and copies/moves them into data member of newly generated functor:

    std::bind(func,arg1,arg2)
    

    gives you:

    class closure1 {
        Arg1 arg1;
        Arg2 arg2;
    
        void operator()() {
             func(arg1,arg2);
        }
    };
    

    and it provides also function call operator in which, arg(s) are passed by value as Lvalues (4th dot in Member function operator() section of std::bind reference:

    Otherwise, the ordinary stored argument arg is passed to the invokable object as lvalue argument: the argument vn in the std::invoke call above is simply arg and the corresponding type Vn is T cv &, where cv is the same cv-qualification as that of g.

    So this:

    auto f = std::bind(func, 1, "100", std::forward<F>(f2));
    

    generates

    class closure2 {
           int i = 1;
           std::string s = "100";
           std::forward<void(int)> f;
           void operator()() {
                 func(i,s,f);    // [1]
           }
    };
    

    and in [1] is problem, because f as lvalue cannot be bound to rvalue reference declared in:

    void func(int n, std::string s, std::function<void(int)> &&subf)
    

    you can add another overload taking lvalue reference:

    void func2(int n, std::string s, std::function<void(int)> &subf)
    

    and call bind with that version of your overloads.


    There is no problem with bind_front_handler because in this implementation all data members of generated functor are forwarded to a target:

     func(...,std::forward< std::function<void()> >(f));
    

    so f will be casted into rvalue reference, and func can accept this argument.


    Demo