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;
}
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.