解压可变参数模板时避免 "recursive" 函数调用,直到运行时条件

Avoid "recursive" function calls while unpacking variadic templates until runtime condition

基本上,我想在参数包中找到满足某些运行时条件的模板。直觉上,我只想遍历参数包的实例化并找到第一个满足条件的实例。我当前的简化玩具实现演示了我的意思:

首先找到XY的结构满足它们的test()

struct X {
  bool test(int i) {
    flag = i > 10;
    return flag;
  }
  bool flag;
  std::string value = "X satisfied first";
};

struct Y {
  bool test(int i) {
    flag = i > 11;
    return flag;
  }
  bool flag;
  std::string value = "Y satiesfied first";
};

这个结构找到XY的第一个结构满足条件。在此示例中,它将一个整数增加到给定限制,直到其中一个结构报告其 test() 成功。

template <typename... Ts> struct FindFirst {
  static std::string find_first(int limit) {
    return find_first_satisfying(limit, Ts{}...);
  }

  static std::string find_first_satisfying(int limit, Ts... ts) {
    int i = 0;
    bool satisfied = false;
    while (i < limit && !satisfied) {
      satisfied = (ts.test(i) || ...);
      i++;
    }
    return extract(ts...);
  }

  template <typename T, typename... OtherTs>
  static std::string extract(T t, OtherTs... ts) {
    if (t.flag) {
      return t.value;
    } else {
      if constexpr (sizeof...(OtherTs) > 0) {
        return extract(ts...);
      } else {
        return "Nobody satiesfied condition";
      }
    }
  }
};

此实现生成与包中模板一样多的具有不同签名的不同 extract() 函数。它们被 "recursively" 调用并导致深度调用堆栈(取决于令人满意的结构的位置)和大字节码。

有没有一种方法可以构建一个循环(在编译时)来测试参数包的每个实例并适当地停止? 另外,关于如何简化整个结构还有其他建议吗?

我会这样写你的代码:

template <typename ... Ts>
std::string find_first_satisfying(int limit, Ts... ts)
{
    for (int i = 0; i != limit; ++i) {
      std::string res;
      bool found = false;
      ([&](){ if (ts.test(i)) { found = true; res = ts.value; } return found;}() || ...);
      if (found) { return res; }
    }
    return "Nobody satisfied condition";
}

Demo

没有。有可能在 C++23 中不会是这样,但目前还不能保证。

但是真的有问题吗?我看到的唯一问题是代码难以编写和理解。大字节码意义不大,优化器应该能够内联和优化所有内容——只有调试性能会因此受到影响(和编译时间)……除非你以一种使 optimizer/compiler 无法执行的方式编写程序内联它(通过隐藏函数体)。

P.S。您不能以某种方式将 extract 编写为运算符并使用 ... 而不是递归吗?不过,出于各种原因,我认为这是一个坏主意。 (我看到@Jarod42 在另一个答案中是通过 lambda 写的,我觉得不错。)