以下代码无法编译:
#include <functional>
template<class ...Args>
void invoke(Args&&... args)
{
}
template<class ...Args>
void bind_and_forward(Args&&... args)
{
auto binder = std::bind(&invoke<Args...>, std::forward<Args>(args)...);
binder();
}
int main()
{
int a = 1;
bind_and_forward(a, 2);
}
如果我没理解错的话,原因如下:std::bind
复制它的参数,当binder
的operator()
被调用时,它将所有绑定(bind)参数作为 lvalues 传递 - 甚至那些输入 bind
的参数作为 rvalues。但是 invoke
是为原始参数实例化的,它不能接受 binder
试图传递给它的内容。
这个问题有解决办法吗?
最佳答案
你的理解是正确的- bind
复制它的参数。所以你必须提供正确的重载 invoke()
将在左值上调用:
template<class ...Args>
void bind_and_forward(Args&&... args)
{
auto binder = std::bind(&invoke<Args&...>, std::forward<Args>(args)...);
^^^^^^^^
binder();
}
这适用于大多数类型。 [func.bind.bind] 中为 operator()
列举了一些异常(exception)情况, 其中Arg&
是不够的。正如您所指出的,其中之一是 std::reference_wrapper<T>
.我们可以通过替换 Args&
来解决这个问题上面的用法与类型特征。通常,我们只是添加一个左值引用,但对于 reference_wrapper<T>
,我们只想要 T&
:
template <typename Arg>
struct invoke_type
: std::add_lvalue_reference<Arg> { };
template <typename T>
struct invoke_type<std::reference_wrapper<T>> {
using type = T&;
};
template <typename T>
using invoke_type_t = typename invoke_type<T>::type;
将其重新插入原始解决方案,我们得到适用于 reference_wrapper
的东西也是:
template<class ...Args>
void bind_and_forward(Args&&... args)
{
auto binder = std::bind(&invoke<invoke_type_t<Args>...>,
// ^^^^^^^^^^^^^^^^^^^
std::forward<Args>(args)...);
binder();
}
当然,如果Arg
之一是一个占位符,无论如何都行不通。如果它是绑定(bind)表达式,您还必须编写其他内容。
关于c++ - std::bind 和完美转发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30968573/