在可变参数模板中使用 std::bind 完美转发引用
Perfect forwarding of references with std::bind inside variadic template
我在我的代码中偶然发现了一个错误,我追溯到 std::bind
"...are never passed by reference unless wrapped in std::ref
or std::cref
".
的参数这一事实
我有什么
我有一个看起来像这样的函数模板(去除了不相关的位):
template<typename F, typename ... Args>
auto eval(F&& _f, Args&&... _args)
{
using ret_t = typename std::result_of<F&&(Args&&...)>::type;
std::function<ret_t()> func(std::bind(std::forward<F>(_f), std::forward<Args>(_args)...)));
// Etc.
}
到目前为止一切顺利,但是如果函数 _f
将引用作为参数,这就会崩溃,因为参数被复制或移动,除非包含在 std::ref
或 std::cref
中。
我想要什么
一种将参数作为对 _f
的引用传递的方法,同时尽可能保留完美转发。我非常想通过将每个 _args
包装在原点的 std::ref
中来避免必须通过每个 _args
。相反,我希望在 eval()
.
中自动计算出引用
我试过的
当 bind
ing _args
似乎有效时,使用 std::ref
而不是 std::forward
:
template<typename F, typename ... Args>
auto eval(F&& _f, Args&&... _args)
{
using ret_t = typename std::result_of<F&&(Args&&...)>::type;
// Replace std::forward<Args>() with std::ref()
std::function<ret_t()> func(std::bind(std::forward<F>(_f), std::ref(_args)...)));
// Etc.
}
但我不知道这对右值引用有何影响。总是使用 std::ref
安全吗?这是矫枉过正吗?我还有完美转发吗?问题比比皆是。
我也考虑过用通用的 lambda 替换 std::bind
,但我不确定如何捕获参数以实现完美转发。
如有任何见解,我们将不胜感激。
Is it safe to always use std::ref
?
它和正常使用引用一样安全。所以在这种情况下,如果 func
没有超过函数 eval()
,那么它是安全的。即使我传入右值,引用也不会悬空。但是,如果您需要在某处存储 func
,那么这就是悬挂引用的秘诀。
您想要做的是有条件地将它们包装为参考。一种方法是为左值和右值提供两个重载:
template <class T> std::reference_wrapper<T> maybe_wrap(T& val) { return std::ref(val); }
template <class T> T&& maybe_wrap(T&& val) { return std::forward<T>(val); }
左值变为引用包装器,右值保留为右值引用。现在:
std::function<ret_t()> func(std::bind(std::forward<F>(_f),
maybe_wrap(std::forward<Args>(_args))...));
安全。
请注意,调用绑定表达式的方式与您确定 return 类型的方式并不完全匹配。所有绑定参数都作为左值传递,所以你真正需要的是:
using ret_t = typename std::result_of<F&(Args&...)>::type;
// ^^^^^^^^^^^
您也可以将其替换为 lambda,这很尴尬,因为捕获参数包的方式有限。你必须通过一个元组并实现 std::apply()
(这在 C++14 中是可行的):
[f=std::forward<F>(f),
args=std::make_tuple(std::forward<Args>(_args)...)]() -> decltype(auto)
{
return std::apply(f, args);
}
不过,由于存在必要的障碍,而且无论如何都会被类型擦除,也许 bind()
更好。
我在我的代码中偶然发现了一个错误,我追溯到 std::bind
"...are never passed by reference unless wrapped in std::ref
or std::cref
".
我有什么
我有一个看起来像这样的函数模板(去除了不相关的位):
template<typename F, typename ... Args>
auto eval(F&& _f, Args&&... _args)
{
using ret_t = typename std::result_of<F&&(Args&&...)>::type;
std::function<ret_t()> func(std::bind(std::forward<F>(_f), std::forward<Args>(_args)...)));
// Etc.
}
到目前为止一切顺利,但是如果函数 _f
将引用作为参数,这就会崩溃,因为参数被复制或移动,除非包含在 std::ref
或 std::cref
中。
我想要什么
一种将参数作为对 _f
的引用传递的方法,同时尽可能保留完美转发。我非常想通过将每个 _args
包装在原点的 std::ref
中来避免必须通过每个 _args
。相反,我希望在 eval()
.
我试过的
当 bind
ing _args
似乎有效时,使用 std::ref
而不是 std::forward
:
template<typename F, typename ... Args>
auto eval(F&& _f, Args&&... _args)
{
using ret_t = typename std::result_of<F&&(Args&&...)>::type;
// Replace std::forward<Args>() with std::ref()
std::function<ret_t()> func(std::bind(std::forward<F>(_f), std::ref(_args)...)));
// Etc.
}
但我不知道这对右值引用有何影响。总是使用 std::ref
安全吗?这是矫枉过正吗?我还有完美转发吗?问题比比皆是。
我也考虑过用通用的 lambda 替换 std::bind
,但我不确定如何捕获参数以实现完美转发。
如有任何见解,我们将不胜感激。
Is it safe to always use
std::ref
?
它和正常使用引用一样安全。所以在这种情况下,如果 func
没有超过函数 eval()
,那么它是安全的。即使我传入右值,引用也不会悬空。但是,如果您需要在某处存储 func
,那么这就是悬挂引用的秘诀。
您想要做的是有条件地将它们包装为参考。一种方法是为左值和右值提供两个重载:
template <class T> std::reference_wrapper<T> maybe_wrap(T& val) { return std::ref(val); }
template <class T> T&& maybe_wrap(T&& val) { return std::forward<T>(val); }
左值变为引用包装器,右值保留为右值引用。现在:
std::function<ret_t()> func(std::bind(std::forward<F>(_f),
maybe_wrap(std::forward<Args>(_args))...));
安全。
请注意,调用绑定表达式的方式与您确定 return 类型的方式并不完全匹配。所有绑定参数都作为左值传递,所以你真正需要的是:
using ret_t = typename std::result_of<F&(Args&...)>::type;
// ^^^^^^^^^^^
您也可以将其替换为 lambda,这很尴尬,因为捕获参数包的方式有限。你必须通过一个元组并实现 std::apply()
(这在 C++14 中是可行的):
[f=std::forward<F>(f),
args=std::make_tuple(std::forward<Args>(_args)...)]() -> decltype(auto)
{
return std::apply(f, args);
}
不过,由于存在必要的障碍,而且无论如何都会被类型擦除,也许 bind()
更好。