避免运行时编译时数字参数转换的代码重复

ein*_*ica 5 c++ templates idiomatic template-specialization template-meta-programming

假设我们有诸如此类的功能

template <typename T, unsigned N> void foo();
Run Code Online (Sandbox Code Playgroud)

为简单起见,假设我们知道只有(常量)值N_1,N_2...... N_k有效N.

现在,假设我想将编译时参数设置为运行时参数,使用foo()黑盒,即实现:

template <typename T> void foo(unsigned n);
Run Code Online (Sandbox Code Playgroud)

通过foo<,>()打电话.我应该怎么做呢?显然,我可以写:

template <typename T> void foo(unsigned n) {
    switch(n) {
    case N_1 :  foo<T, N_1>(); break;
    case N_2 :  foo<T, N_2>(); break;
    // etc. etc.
    case N_k :  foo<T, N_k>(); break;
    }
 }
Run Code Online (Sandbox Code Playgroud)

......但这让我感到很脏.我想,我可以使用MAP()元宏来生成这些k行; 但我可以做更好的事情,而不是那么做宏观的事情吗?是否有可能编写类似上面的通用,并适用于每个可变参数模板和固定值的常量值?

笔记:

  • C++ 11/14/17特定的建议显然是受欢迎的.
  • N不一定是连续的,也不是小的,也不是排序的.例如,假设N_2 = 123456789且N_5 = 1.

Bar*_*rry 6

你可以创建一个函数指针表:

using F = void(*)();

template <class T, class >
struct Table;

template <class T, size_t... Is>
struct Table<T, std::index_sequence<Is...> > {
    static constexpr F fns[] = {
        foo<T, Is>...
    };
};

template <class T, size_t... Is>
constexpr F Table<T, std::index_sequence<Is...> >::fns[sizeof...(Is)];
Run Code Online (Sandbox Code Playgroud)

然后只需调用你想要的那个:

template <class T, size_t N>
struct MakeTable : Table<T, std::make_index_sequence<N>> { };

template <typename T>
void foo(unsigned n) {
    MakeTable<T, MaxN>::fns[n]();
}
Run Code Online (Sandbox Code Playgroud)

如果N_ks不连续,那么我们可以使用lambda进行内联参数解包:

 template <class T>
 void foo(unsigned n) {
     using seq = std::index_sequence<N_1, N_2, ..., N_k>;
     indexer(seq)([n](auto i){
         if (n == i) {
             f<T, i>();
         }
     });
}
Run Code Online (Sandbox Code Playgroud)

如果上面的速度太慢,那么我想只需手动构建一个std::unordered_map<unsigned, void(*)()>或者其他东西.