允许 lambda/回调函数的多个签名作为模板参数

Allow multiple signatures for lambda/callback function as template parameter

本文关键字:参数 lambda 回调 函数 允许      更新时间:2023-10-16

我想允许类型指定为模板参数的可调用对象使用多个签名。更具体地说,我有一个模板化的update方法,它接受一个必须返回float的可调用对象,并使用它来更新数据网格中的值。对此的简化说明是

template <typename Fn>
void update(Fn&& fn_)
{
    for (Vec3 pos : m_grid)
    {
        float val = fn_(pos, m_grid)
        m_grid(pos) = val;
        ...

在上述情况下,fn_的签名必须始终同时具有 pos 和网格作为参数,即使在实现中忽略它们也是如此

我想使用一些模板魔术来允许回调签名的多种排列。

特别是,我想允许接受pos但不grid的回调,以及根本不接受参数的回调。 我不介意是否强制执行参数的排序。

有人对如何做到这一点有任何提示吗?我不介意使用 boost 或其他库,但它们应该是仅标题的。

您可以使用 SFINAE 使用 is_invocable 的辅助函数来执行此操作(C++17:std::is_invocable ,或更早的 boost: boost::callable_traits::is_invocable

template <typename Fn,
          std::enable_if_t<std::is_invocable<Fn, Vec3, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
  return fn_(pos_, grid_);
}
template <typename Fn,
          std::enable_if_t<std::is_invocable<Fn, Vec3>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
  return fn_(pos_);
}
template <typename Fn,
          std::enable_if_t<std::is_invocable<Fn, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
  return fn_(grid_);
}
template <typename Fn>
void update(Fn&& fn_)
{
    for (Vec3 pos : m_grid)
    {
        float val = call_helper(fn_, pos, m_grid)
        m_grid(pos) = val;
        ...

特别是,我想允许采用 pos 但不采用网格的回调,以及根本不采用参数的回调。

只需定义两个重载并使用 lambda 通过将请求转发到完整函数来执行此操作,从而过滤额外的参数。
举一个最小的工作示例:

struct S {
    template <typename Fn>
    auto update(Fn &&fn_)
    -> decltype(fn_(0, 0), void())
    {
        // ...
        fn_(0, 0);
        // ...
    }
    template <typename Fn>
    auto update(Fn &&fn_)
    -> decltype(fn_(0), void())
    { update([&fn_](auto a, auto) { fn_(a); }); }
    template <typename Fn>
    auto update(Fn &&fn_)
    -> decltype(fn_(), void())
    { update([&fn_](auto, auto) { fn_(); }); }
};
int main() {
    S s;
    s.update([](auto, auto) {});
    s.update([](auto) {});
    s.update([]() {});
}