多种模板<int>功能的专业化

Specialization of multiple template<int> functions

本文关键字:gt 功能 专业化 int lt      更新时间:2023-10-16

我运行的模拟需要使用int参数的模板(D=系统的维度)。典型的模拟功能是

template <int D> void simulation();

当我想专门化这个模板时,我使用一个开关

switch(d){
case 2:
    simulation<2>();
    break;
case 3:
    simulation<3>();
    break;
// etc.
}

就我有一个模拟函数而言,这是可以的。但想象一下,我有10个(simul1,simul2,…simul10),d可以从2到10。我必须写十次相同的开关!

我想知道是否有可能对其进行因子分解,并得到类似的东西:

template <void (*fun)()> runSimulation(int d){
    switch(d){
    case 2:
        fun<2>();
    }
}

当然,<void (*fun)()>不能做我想做的事,因为funtemplate<int>。有办法做到吗?

当您可以将模拟函数更改为具有静态方法的类时:

struct sim1
{
    template<int D> static void apply();
};

然后以下内容应该起作用:

template <typename Sim> runSimulation(int d){
    switch(d){
    case 2:
        Sim::template apply<2>();
    case 3:
        Sim::template apply<3>();
    // ...
    }
}

它是通用的,可以用runSimulation<sim1>(d);runSimulation<sim2>(d);等调用。

以下是一个使用模板递归将运行时信息转换为编译时信息的简单方案:

template<int N> void runSimulation() { std::cout<<"runSimulation " << N << std::endl; }
constexpr size_t max_index = 100;
namespace detail
{
   //overload for maximum index
    void callSimulation_impl(size_t i, std::integral_constant<size_t, max_index>) {}
    template<size_t N>
    void callSimulation_impl(size_t i, std::integral_constant<size_t, N>)
    {
        if(i==N)
        {
            runSimulation<N>();
        }
        else
        {
            callSimulation_impl(i, std::integral_constant<size_t, N+1>());
        }
    }
}
void callSimulation(size_t i)
{
    detail::callSimulation_impl(i, std::integral_constant<size_t, 0>());
}
int main(int argc, char *argv[])
{
    callSimulation(10);                  // calls runSimulation<10>();
    //or also:
    callSimulation(rand()%max_index);    //calls a random simulation
}

演示

就像您生成的手动切换一样,它需要传递索引大小的线性努力(使用二进制搜索也可以实现类似的算法)。

如果效率很重要,你也可以通过使用魔术开关来实现O(1),看看这里和这里——我是它的忠实粉丝


编辑:此方案可以与@Daniel Frey的另一个答案相结合,以便为多种模拟类型和任意多个切换情况获得单个函数。

如果C++接受模板函数作为模板参数,那就太好了,但我不知道如何表达。但是,它接受模板类作为模板参数。

如果您愿意将模拟系列(simul1、simul2等)打包到每个系列(Wrapper1、Wrapper2等)的一个包装器模板中,您可以执行以下操作:

template<template<int D> class SimulationFunctionWrapper> struct Caller {
    static void simulation(int d) {
        switch(d) {
            case 2: SimulationFunctionWrapper<2>::run(); break;
            case 3: SimulationFunctionWrapper<3>::run(); break;
        }
    }
};
#include <iostream>
// normal simul1 declarations and definitions
template<int D> void simul1();
template<> void simul1<2>() { std::cout << "simul1<2>n"; }
template<> void simul1<3>() { std::cout << "simul1<3>n"; }
// Enables dispatching to the right simul1 based on the template integer
template<int D> struct Wrapper1 { static void run() { simul1<D>(); } };
// normal simul2 declarations and definitions
template<int D> void simul2();
template<> void simul2<2>() { std::cout << "simul2<2>n"; }
template<> void simul2<3>() { std::cout << "simul2<3>n"; }
// Enables dispatching to the right simul2 based on the template integer
template<int D> struct Wrapper2 { static void run() { simul2<D>(); } };
int main(int argc, const char *argv[]) {
    Caller<Wrapper1>::simulation(argc);
    Caller<Wrapper2>::simulation(argc);
    return 0;
}

这显示了如何使用相同的代码(Caller)来决定调用哪个单独的函数。不幸的是,它需要样板包装。