问题描述
假设有一个模板函数 foo()可以接受任意数量的参数。鉴于最后一个参数始终是 std :: function ,如何实现下面所示的 foo()模板, CbArgs 包含此 std :: function 参数的方式?
Assume there is a template function foo() which accepts an arbitrary number of arguments. Given the last argument is always an std::function, how do I implement a foo() template shown below in a way that CbArgs would contain this std::function's parameters?
template<typename... InArgs, typename... CbArgs = ???> // ^^^^^^^^^^^^ void foo(InArgs... args) { ... }
例如, CbArgs 应该为 {int,int} 如果像这样调用:
For example, CbArgs should be {int,int} if invoked like this:
std::function<void(int,int)> cb; foo(5, "hello", cb);
我的第一个想法是:
template<typename... InArgs, typename... CbArgs> void foo(InArgs... args, std::function<void(CbArgs...)>) { ... }
但这无法编译:
note: template argument deduction/substitution failed: note: mismatched types ‘std::function<void(CbArgs ...)>’ and ‘int’ foo(5, "hello", cb);
问题一:
为什么不这个编译?为什么模板参数推导失败?
Question One:
Why doesn't this compile? Why does the template argument deduction fail?
最终,我提出了以下解决方案:
Eventually, I came up this solution:
template<typename... InArgs, typename... CbArgs> void fooImpl(std::function<void(CbArgs...)>, InArgs... args) { ... } template<typename... InArgs, typename CbType = typename std::tuple_element_t<sizeof...(InArgs)-1, std::tuple<InArgs...>>> void foo(InArgs... args) { fooImpl(CbType{}, args...); }
此处 CbType 是 InArgs 中的最后一个类型是 std :: function 。然后将 CbType 的临时变量传递给 fooImpl(),其中 CbArgs 被推导。这行得通,但对我来说却很难看。
Here CbType is the last type in InArgs which is std::function. Then a temporary of CbType is passed to fooImpl() where CbArgs are deduced. This works, but looks ugly to me.
问题二:
我想知道如果没有更好的解决方案有两个功能和 CbType 的临时实例?
推荐答案
当 parameter pack 不是最后一个参数时,无法推论。告诉编译器 InArgs ... 的内容将使您的 foo 定义起作用:
When a parameter pack is not the last parameter, it cannot be deduced. Telling the compiler the contents of InArgs... will make your foo definition work:
template<typename... InArgs, typename... CbArgs> void foo(InArgs..., std::function<void(CbArgs...)>) { } int main() { std::function<void(int,int)> cb; foo<int, const char*>(5, "hello", cb); }
或者,正如您在解决方法中发现的那样,只需将 InArgs ... 最后更新您的 foo 调用:
Alternatively, as you discovered in your workaround, simply put InArgs... at the end and update your foo invocation:
template<typename... InArgs, typename... CbArgs> void foo(std::function<void(CbArgs...)>, InArgs...) { } int main() { std::function<void(int,int)> cb; foo(cb, 5, "hello"); }
这是避免不必要的临时实例的一种可能方法,但使用相同的机制来扣除 CbArgs ... :只需包装 CbType 在一个空包装中,然后将其传递给 fooImpl 。
Here's a possible way of avoiding the unnecessary temporary instance but using your same mechanism for the deduction of CbArgs...: simply wrap CbType in an empty wrapper, and pass that to fooImpl instead.
template <typename T> struct type_wrapper { using type = T; }; template<typename... InArgs, typename... CbArgs> void fooImpl(type_wrapper<std::function<void(CbArgs...)>>, InArgs&&...) { } template<typename... InArgs, typename CbType = std::tuple_element_t<sizeof...(InArgs)-1, std::tuple<std::remove_reference_t<InArgs>...>>> void foo(InArgs&&... args) { fooImpl(type_wrapper<CbType>{}, std::forward<InArgs>(args)...); }
其他改进:
-
在 typename CbType = 之后的 typename 是不必要的-已将其删除
The typename after typename CbType = was unnecessary - it was removed.
args ... 应该完美地转发到 fooImpl 保留其值类别。 foo 和 fooImpl 都应使用 args ... 作为转发参考。
args... should be perfectly-forwarded to fooImpl to retain its value category. Both foo and fooImpl should take args... as a forwarding-reference.
请注意,有一项建议可以使处理非终端参数包的方式更容易:。这样可以使您的原始实现按预期工作。
Note that there is a proposal that would make dealing with non-terminal parameter packs way easier: P0478R0 - "Template argument deduction for non-terminalfunction parameter packs". That would make your original implementation work as intended.
这篇关于模板参数和std :: function参数的推论的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!