切换模板参数

switch over template parameters

当函数的模板参数的值仅在 运行 时已知时,必须编写考虑所有可能参数的代码,并相应地调用函数。

这个(过度)简化的例子说明了问题

#include <iostream>

template <int i>
void f()
{
   std::cout << i << std::endl;
}

int main()
{
   int i;
   std::cin >> i;
   if (i==0)
      f<0>();
   else if (i==1)
      f<1>();
   else if (i==2)
      f<2>();
   else
      std::cout << "invalid input" << std::endl;
}

这显然非常乏味,尤其是在有很多可能值的情况下。更糟糕的是,如果模板参数大于 1,则需要考虑所有组合,例如:

#include <iostream>

template <int i, bool X>
void f()
{
   std::cout << i << ' ';
   if constexpr (X)
      std::cout << "true" << std::endl;
   else
      std::cout << "false" << std::endl;
}

int main()
{
   int i;
   char c;

   std::cin >> i;
   std::cin >> c;    
   if (c == 'y')
      {
         if (i==0)
            f<0,true>();
         else if (i==1)
            f<1,true>();
         else if (i==2)
            f<2,true>();
         else
            std::cout << "invalid input" << std::endl;
      }
   else
      {
         if (i==0)
            f<0,false>();
         else if (i==1)
            f<1,false>();
         else if (i==2)
            f<2,false>();
         else
            std::cout << "invalid input" << std::endl;
      }
}

(我当然知道滥用这个想法,即为所有模板组合实例化 f,可能会导致代码膨胀。)

我正在寻找一个宏,它允许“基本上”用 ix 一些 运行 时间变量编写等同于 f<i, x>() 的内容。

更具体地说,假设所有模板参数的值都是有限的,比如说

i = i1, i2, ... , in
x = x1, x2, ... , xn

我想要一个可以翻译类似这样内容的宏

CallF(f, i, {i1,...,in}, x, {x1,...,xn}, par...)

进入

if (i==i1)
{
    if (x==x1)
      f<i1,x1>(par...);
    else if (x==x2)
      f<i1,x2>(par...);
    ....
}
else if (i==i2)
{
    if (x==x1)
      f<i2,x1>(par...);
    else if (x==x2)
      f<i2,x2>(par...);
    ....
}
else if (i==i3)
....

这是一种针对 1 个模板参数执行此操作的方法:

#include <cstdio>
#include <stdexcept>
#include <utility>

template <int i>
void f(char const* s, bool b, double d) {
  std::printf("%d -> %s %d %f\n", i, s, b, d);
}

template <class T, class ...Args>
[[noreturn]] void call_f(T&&, Args&&...) {
  throw std::invalid_argument{"Argument not in the list"};
}

template <auto first, auto... rest, class T, class ...Args>
void call_f(T const& t, Args&&... args) {
  if (t == first)
    f<first>(std::forward<Args>(args)...);
  else
    call_f<rest...>(t, std::forward<Args>(args)...);
}

int main() {
  int i = 2; // template argument

  // other arguments for f
  char const* s = "Hello";
  bool b = false;
  double d = 3.14;

  // decide which one to call at run time.
  call_f<1, 2, 3>(i, s, b, d);
}