std::function 参数的模板参数和推导
Template argument and deduction of std::function parameters
假设有一个模板函数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
?
的临时实例,是否有更好的解决方案
Why doesn't this compile? Why does the template argument deduction fail?
当参数包不是最后一个参数时,无法推导。告诉编译器 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");
}
I wonder if there is a better solution without having two functions and a temporary instance of 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 CbType =
之后的 typename
是不必要的 - 它已被删除。
args...
应为 perfectly-forwarded 至 fooImpl
以保留其 值类别 。 foo
和 fooImpl
都应该把 args...
作为 forwarding-reference.
请注意,有一项提案可以使 non-terminal 参数包的处理更加容易:P0478R0 - “non-terminal 的模板参数推导”
函数参数包。这将使您的原始实现按预期工作。
假设有一个模板函数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
?
Why doesn't this compile? Why does the template argument deduction fail?
当参数包不是最后一个参数时,无法推导。告诉编译器 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");
}
I wonder if there is a better solution without having two functions and a temporary instance of
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 CbType =
之后的typename
是不必要的 - 它已被删除。args...
应为 perfectly-forwarded 至fooImpl
以保留其 值类别 。foo
和fooImpl
都应该把args...
作为 forwarding-reference.
请注意,有一项提案可以使 non-terminal 参数包的处理更加容易:P0478R0 - “non-terminal 的模板参数推导” 函数参数包。这将使您的原始实现按预期工作。