假设有一个模板函数foo()
,它接受任意数量的参数。鉴于最后一个参数始终为std::function
,如何以foo()
包含此CbArgs
参数的方式实现下面显示的std::function
模板?
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
的临时实例?
答案 0 :(得分:3)
为什么不编译?为什么模板参数推导失败?
当参数包不是最后一个参数时,无法推断。告诉编译器InArgs...
的内容将使foo
定义起作用:
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
调用:
template<typename... InArgs, typename... CbArgs>
void foo(std::function<void(CbArgs...)>, InArgs...) { }
int main()
{
std::function<void(int,int)> cb;
foo(cb, 5, "hello");
}
我想知道如果没有两个函数和
CbType
的临时实例,是否有更好的解决方案?
这是避免不必要的临时实例的可能方法,但使用相同的机制来推导CbArgs...
:只需将CbType
包装在空包装中,然后将其传递给fooImpl
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
之后的typename CbType =
是不必要的 - 它已被删除。
args...
应完美转发至fooImpl
以保留其值类别。 foo
和fooImpl
都应将args...
作为转发参考。
请注意,有一项提案可以更轻松地处理非终端参数包:P0478R0 - "Template argument deduction for non-terminal function parameter packs"。这将使您的原始实现按预期工作。