std::function 可变参数合一 vector/map

std::function variable arguments in one vector/map

如果不使用两个单独的支架,今天如何在 c++ 中做到这一点?

typedef std::function<void(int a, int b)> f1; 
typedef std::function<void(int a)> f2; 

std::vector<f1> m;

void add(f1 f)
{
    m.push_back(f);
}

void add(f2 f)
{
    // add one more (unused) parameter to f2 so we can add f2 to f1 vector holder?
}

我们能否以某种方式重载 f1 函数以包含不同的参数集? 现在可以通过可变模板或类似的东西解决这个问题吗?

创建一个与新签名匹配的新 lambda 并改为添加:

void add(f2 f)
{
    m.push_back( [g = std::move(f)](int a, int /* unused */){ g(a); } );
}

这将 std::function 包装在忽略任何额外参数的 lambda 中:

template<class R, class...Args>
auto ignore_extra_args( std::function<R(Args...)> f ) {
  return [f = std::move(f)](Args...args, auto&&...)->R{
    return f(std::forward<Args>(args)...);
  };
}

如果我们不想要那种不必要的类型擦除层,它会变得更加棘手。

您需要找到可以调用给定对象 fArgs... 的最长前缀,然后用它调用 f。这涉及一些棘手的元编程。

如果让调用方传入签名就简单多了:

template<class Sig>
struct ignore_extra_args_helper;
template<class R, class...Args>
struct ignore_extra_args_helper<R(Args...)> {
  template<class F>
  auto operator()( F&& f ) {
    return [f = std::forward<F>(f)](Args...args, auto&&...)->R{
      return f(std::forward<Args>(args)...);
    };
  }
};
template<class Sig, class F>
auto ignore_extra_args( F&& f ) {
  return ignore_extra_args_helper<Sig>{}(std::forward<F>(f));
}

这节省了可能的开销。

template<class F, decltype(std::declval<F const&>()(1,1))* =nullptr>
void add(F&& f) {
  m.push_back(std::forward<F>(f));
}

template<class F, class...Unused>
void add(F&& f, Unused&&...) {
  add( ignore_extra_args<void(int)>(std::forward<F>(f)) );
}

live example