使用标准::绑定在可变参数模板中完美转发引用
Perfect forwarding of references with std::bind inside variadic template
我偶然发现了代码中的一个错误,我将其追溯到这样一个事实,即参数std::bind
"...除非用std::ref
或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
的引用传递的方法,同时尽可能保持完美的转发。我非常想通过在原点将其包裹在std::ref
中来避免通过每个_args
。相反,我希望在eval()
内部自动找出引用。
我试过什么
bind
_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
,但我不确定如何捕获参数以实现完美的转发。
任何见解将不胜感激。
始终使用
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))...));
是安全的。
注意,调用绑定表达式的方式和确定返回类型的方式并不完全匹配。所有绑定参数都作为左值传递,因此您真正需要的是:
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()
更好。
- 将函数参数完美转发到函数指针:按值传递呢?
- C++20理念:要求表达和完美转发
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 返回值的完美转发?
- 使用衰减与完美转发
- 可变参数模板:将整数参数完美转发到 lambda
- 完美转发C++重载和模板化函子及其参数
- 在完美转发中需要衰减
- C++完美转发:如何避免悬空引用
- 无法理解一段具有完美转发和省略号的C++代码
- 在编写包装现有函数并检查错误的模板函数时,如何使用完美转发?
- 完美转发可变参数模板模板
- 在完美转发函数中公开参数类型,避免代码重复
- 使用完美转发的模板转换构造函数
- 完美转发使用结构化绑定声明的变量
- 完美转发常量参数以进行持续评估
- 使用auto&&完美转发返回值
- 完美转发可变参数模板参数到成员函数
- 无需使用 ODR 即可实现完美转发
- 使用模板类完美转发