【问题标题】:Pass variadic std::function传递可变参数 std::function
【发布时间】:2018-10-10 14:07:32
【问题描述】:

我有一个接受回调的函数。它应该适用于函数 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?

【问题讨论】:

  • 您可以将函数特征创建为argument_of,但您会受到限制:您不再接受泛型 lambda 和泛型仿函数,或具有重载的仿函数。
  • 您系统中的所有命令必须具有相同的已知签名。如果您认为需要支持任意签名,那么您的设计还没有考虑周全。
  • 你将如何编写调用func的代码?
  • 它是一个命令处理器。我用空格分隔的参数解析一个字符串,这些参数被相应地强制转换以适合函数签名。
  • 我建议template&lt;typename... args, typename t_func&gt; void add_command(const std::string&amp; name, t_func func),然后您只需在调用函数时将参数类型指定为模板参数。

标签: c++ c++11 templates template-argument-deduction


【解决方案1】:

std::function 是一个类型擦除模板。类型推导与类型擦除相反(几乎相反)。

类型推断类型擦除模板是代码异味。而且它很少起作用。

里有扣分指南,可以这样操作:

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);
}

这是不完美的,但不可能有完美的解决方案。

推理指导look like:

template<class R, class... ArgTypes>
function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>;
template<class F>
function(F) -> function</*see below*/>;

它通过检查&amp;F::operator() 提取function 的签名。这可能会因重载或模板而失败。当然,这不适用于重载的函数名称。

您可以在 中使用函数特征类复制此内容:

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 限定符(&amp;、&amp;&amp; 什么都没有),然后是 4 个其他必须“手动”处理的东西(const、volatile、noexcept 和 ... C 风格的可变参数)。

无论如何,一旦你有了它,你就可以这样做:

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);
}

在。这(大致和不完美)与 演绎指南相同。

Live example.

【讨论】:

  • 很好地使用了演绎指南。正如你所说,它并不完美,但它适用于许多情况。 +1
  • 还有 C 省略号案例 (ala printf) 用于使样板加倍的特征:-/
  • @Jarod42 重写为乘法
猜你喜欢
  • 2018-03-07
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-20
相关资源
最近更新 更多