【发布时间】:2017-02-19 22:34:10
【问题描述】:
假设有一个模板函数foo() 接受任意数量的参数。鉴于最后一个参数始终是std::function,我如何以CbArgs 包含此std::function 参数的方式实现下面显示的foo() 模板?
template<typename... InArgs, typename... CbArgs = ???>
// ^^^^^^^^^^^^
void foo(InArgs... args) { ... }
例如,CbArgs 应该是 {int,int},如果这样调用:
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);
问题一:
为什么不编译?为什么模板参数推导失败?
最终,我想出了这个解决方案:
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。这行得通,但在我看来很难看。
问题二:
我想知道没有两个函数和CbType的临时实例是否有更好的解决方案?
【问题讨论】:
-
但该功能是否必须位于最后位置?还是可以排第一?
-
它必须在最后一个位置。这是包装异步函数所必需的,这些函数的最后一个参数是回调。
-
"必须" -- 对不起,我可以编写一个没有回调作为最后一个参数的异步函数。那么它以何种方式“必须”将回调作为其最后一个参数?只是你更喜欢连续传球风格吗?你认为“接下来会发生什么”是在“你在做什么”之后?原因有很多,但是“异步函数必须有一个回调作为最后一个参数”跳过了原因,只是陈述了你的结论。
-
您是否看过 n3865 并考虑过这种方法?
-
好吧,让我换个说法。从技术上讲,回调不必是最后一个参数。但通常是最后一个参数。如果你认为我错了,请给我在现实生活中相反的例子。无论如何,我想要的只是编写一个包装器,它将许多已经编写的函数转换为最后一个位置的回调函数,这些函数将返回
future。例如。convertToFuture(&foo, 5, "hello")会调用foo(5, "hello", <some lambda>)并返回future<tuple<int,int>>,这在调用回调时实现。
标签: c++ c++14 variadic-templates template-argument-deduction