#include <functional>
#include <iostream>
#include <type_traits>
using cb_t = std::function<void ()>;
template<typename CB_T, std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, bool> = true>
void
on_my_write(int a, int b, CB_T&& cb) {
std::cout << "on_my_write:" << a << ", " << b << std::endl;
}
template<typename CB_T, std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, bool> = true>
void foo(CB_T&& cb) {
auto b = std::bind(&on_my_write<CB_T>, std::placeholders::_1, std::placeholders::_2, std::forward<CB_T>(cb));
b(1, 2);
}
int main() {
foo([]{});
return 0;
}
compile this with command line
g++ ./try1.cpp -std=c++17
gave me:
./try1.cpp: In instantiation of ‘void foo(CB_T&&) [with CB_T = main()::<lambda()>; typename >std::enable_if<std::is_constructible<std::function<void()>, CB_T>::value, bool>::type <anonymous> = 1]’:
./try1.cpp:39:13: required from here
./try1.cpp:34:6: error: no match for call to ‘(std::_Bind<void (*(std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>))(int, int, main()::<lambda()>&&)>) (int, int)’
b(1, 2);
~^~~~~~
In file included from ./try1.cpp:19:0:
/usr/include/c++/7/functional:547:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
operator()(_Args&&... __args)
^~~~~~~~
/usr/include/c++/7/functional:547:2: note: template argument deduction/substitution failed:
/usr/include/c++/7/functional:558:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
operator()(_Args&&... __args) const
^~~~~~~~
/usr/include/c++/7/functional:558:2: note: template argument deduction/substitution failed:
/usr/include/c++/7/functional:576:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
operator()(_Args&&... __args) volatile
^~~~~~~~
/usr/include/c++/7/functional:576:2: note: template argument deduction/substitution failed:
/usr/include/c++/7/functional:588:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
operator()(_Args&&... __args) const volatile
^~~~~~~~
/usr/include/c++/7/functional:588:2: note: template argument deduction/substitution failed:
But if I change on_my_write(int a, int b, CB_T&& cb)
to on_my_write(int a, int b, const CB_T& cb)
, then it will compile and run successfully.
I really don't understand why?
Could anybody explain this to me? thanks a lot!
I've tried several hours and can't figure it out.
Yes, this is slightly tricky. std::bind
is of course not magical, it has to store the bound parameters somehow and it stores them as values by default, unless requested with std::ref
argument.
Meaning that in the call std::bind(...,std::forward<CB_T>(cb))
, the cb
is used to construct the bound member variable properly using the perfect forwarding.
But, the calling is not done with perfect forwarding sadly, the argument that bind will pass to on_my_write
is always an l-value. It is not impossible to create std::bind
version that would forward it and I do not know exactly why is it this way, but a good reason might be that such forwarding would make calling the bound functor multiple times dangerous.
So, since std::bind
always passes the argument as l-value, you have a couple of options:
on_my_write(int a, int b, const CB_T& cb)
as you did.on_my_write(int a, int b, CB_T& cb)
and use std::move(cb)
inside to consume it. Be careful, this is exactly the case in which the new functor should be called just once. std::bind(&on_my_write<CB_T&>, std::placeholders::_1, std::placeholders::_2, std::forward<CB_T>(cb));
This will work thanks to reference collapsing rules and on_my_write(int a, int b, CB_T& cb)
will be called always. You can still use 2. and move anyway.std::bind
, use lambda: template<typename CB_T, std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, bool> = true> void foo(CB_T&& cb) { auto b = [&cb](auto a,auto b){return on_my_write(a,b,std::forward<CB_T>(cb));}; b(1, 2); }
This will forward cb
properly, an extra advantage of this solution is that you do not have to deduce on_my_write
manually.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.