c++ - 传递可变参数 std::function
问题描述
我有一个接受回调的函数。它应该与函数 ptrs、lambdas(无状态和有状态)等一起使用。我可以执行以下操作:
template<typename t_func>
void add_command(const std::string& name, t_func func)
问题是我需要使用 func 的参数类型。所以我这样做了:
template<typename... t_args>
void add_command(const std::string& name, const std::function<void(t_args...)>& args)
这会产生以下错误:no matching function for call to
...
note: template argument deduction/substitution failed
有什么方法可以传递泛型函数类型,同时仍然可以访问它的参数?我知道std::result_of
,有没有类似的std::arguments_of
?
解决方案
std::function
是一个类型擦除模板。类型推导与类型擦除相反(几乎相反)。
类型推断类型擦除模板是代码气味。而且它很少起作用。
在c++17中有一个演绎指南,所以你可以这样做:
template<typename... t_args>
void add_command(const std::string& name, const std::function<void(t_args...)>& args)
void add_command(const std::string& name, t_func const& func) {
std::function f = func;
add_command(name, f);
}
它是不完美的,但不可能有完美的解决方案。
扣除指南看起来像:
template<class R, class... ArgTypes>
function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>;
template<class F>
function(F) -> function</*see below*/>;
它在其中提取function
via 检查的签名&F::operator()
。这可能会因重载或模板而失败。当然,这不适用于重载的函数名称。
您可以使用函数特征类在c++11中复制它:
template<class X>
struct function_traits:function_traits<decltype(&X::operator())> {};
#define MEM_FUN_HELPER2(...) \
template<class R, class T, class...Args> \
struct function_traits<R(T::*)(Args...) __VA_ARGS__>:function_traits<R(Args...)>{}; \
template<class R, class T, class...Args> \
struct function_traits<R(T::*)(Args..., ...) __VA_ARGS__>:function_traits<R(Args..., ...)>{}; \
template<class R, class T, class...Args> \
struct function_traits<R(T::*)(Args...) __VA_ARGS__ noexcept>:function_traits<R(Args...) noexcept>{}; \
template<class R, class T, class...Args> \
struct function_traits<R(T::*)(Args..., ...) __VA_ARGS__ noexcept>:function_traits<R(Args..., ...) noexcept>{}
#define MEM_FUN_HELPER1(...) \
MEM_FUN_HELPER2(__VA_ARGS__); \
MEM_FUN_HELPER2(__VA_ARGS__ &); \
MEM_FUN_HELPER2(__VA_ARGS__ &&)
#define MEM_FUN_HELPER0(...) \
MEM_FUN_HELPER1(__VA_ARGS__); \
MEM_FUN_HELPER1(const __VA_ARGS__)
#define MEM_FUN_HELPER() \
MEM_FUN_HELPER0(); \
MEM_FUN_HELPER0(volatile)
MEM_FUN_HELPER();
template<class R, class...Args>
struct function_traits<R(*)(Args...)>:function_traits<R(Args...)>{};
template<class R, class...Args>
struct function_traits<R(*)(Args..., ...)>:function_traits<R(Args..., ...)>{};
template<class R, class...Args>
struct function_traits<R(*)(Args...) noexcept>:function_traits<R(Args...) noexcept>{};
template<class R, class...Args>
struct function_traits<R(*)(Args..., ...) noexcept>:function_traits<R(Args..., ...) noexcept>{};
template<class R, class...Args>
struct function_traits<R(Args...) noexcept> : function_traits<R(Args...)> {
enum {is_noexcept=true};
};
template<class R, class...Args>
struct function_traits<R(Args..., ...) noexcept> : function_traits<R(Args..., ...)> {
enum {is_noexcept=true};
};
template<class R, class...Args>
struct function_traits<R(Args...)> {
template<template<class...>class Z>
using transcribe=Z<R(Args...)>;
using std_function = transcribe<std::function>;
using result_type = R;
using arg_tuple = std::tuple<Args...>;
enum{is_noexcept=false};
};
template<class R, class...Args>
struct function_traits<R(Args..., ...)> {
template<template<class...>class Z>
using transcribe=Z<R(Args..., ...)>;
using std_function = transcribe<std::function>;
using result_type = R;
// doesn't really work, but what ya gonna do:
using arg_tuple = std::tuple<Args...>;
enum{is_noexcept=false};
};
这太疯狂了;MEM_FUN_HELPER();
扩展为 48 个模板专业。3 个 ref 限定符(&
和&&
什么都没有),然后是 4 个必须“手动”处理的其他东西( 、 和const
Cvolatile
风格的可变参数)。noexcept
...
无论如何,一旦你有了它,你可以这样做:
template<typename... t_args>
void add_command(const std::string& name, const std::function<void(t_args...)>& args)
template<class t_func>
void add_command(const std::string& name, t_func const& func) {
typename function_traits<t_func>::std_function f = func;
add_command(name, f);
}
推荐阅读
- reactjs - 将 React 应用程序注入 Chrome 扩展程序时,无法在“历史记录”上执行“pushState”
- php - 在默认 var/www/html 文件夹内的实时服务器中,如果没有 index.php,codeigniter 路由将无法正常工作
- apache - 是否有任何功能可以更改网站的 URL 路径?
- django - 在 Google Cloud Platform 中部署 Django Channels App
- html - 如何停止 HTML 到 JSP 页面重定向的 cookie 操作?
- reactjs - this.component.name TypeError:无法读取未定义的属性“名称”
- c# - 无法在 EF Where 表达式中使用 LINQ 用于 lambda 属性
- maven - 我无法运行 jenkins 作业来运行 maven 项目?
- arrays - Blade Url中的Laravel关联数组
- webpack - 用于为每个块提取语言包的 Webpack 插件