#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;
}
用命令行编译这个g++ ./try1.cpp -std=c++17
给我:./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:
但是如果我将 on_my_write(int a, int b, CB_T&& cb)
更改为 on_my_write(int a, int b, const CB_T& cb)
,那么它将成功编译并运行。我真的不明白为什么?
有人可以向我解释一下吗?多谢!
我已经尝试了几个小时,但无法弄清楚。
最佳答案
是的,这有点棘手。 std::bind
当然不是神奇的,它必须以某种方式存储绑定(bind)参数,并且默认情况下将它们存储为值,除非使用 std::ref
参数请求。
这意味着在调用 std::bind(...,std::forward<CB_T>(cb))
中,cb
用于使用完美转发正确构造绑定(bind)成员变量。
但是,遗憾的是,调用并没有通过完美转发完成,bind 将传递给 on_my_write
的参数始终是一个左值。创建将转发它的 std::bind
版本并非不可能,我不知道为什么会这样,但一个很好的理由可能是这样的转发会使多次调用绑定(bind)仿函数变得危险。
因此,由于 std::bind
始终将参数作为左值传递,因此您有几个选择:
on_my_write(int a, int b, const CB_T& cb)
。 on_my_write(int a, int b, CB_T& cb)
并在里面使用 std::move(cb)
来消费它。小心,这正是应该只调用一次新仿函数的情况。 std::bind(&on_my_write<CB_T&>, std::placeholders::_1, std::placeholders::_2,
std::forward<CB_T>(cb));
由于引用折叠规则,这将起作用,并且 on_my_write(int a, int b, CB_T& cb)
将始终被调用。你仍然可以使用 2. 并移动。 std::bind
,使用 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);
}
这将正确转发 cb
,此解决方案的一个额外优点是您不必手动推断 on_my_write
。 关于c++ std::bind 通用引用无法编译,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/65177439/