【问题标题】:Specialization of multiple template<int> functions多个模板<int>函数的特化
【发布时间】:2015-06-25 22:57:31
【问题描述】:

我正在运行的模拟要求我为 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>();
    }
}

当然&lt;void (*fun)()&gt; 不符合我的要求,因为funtemplate&lt;int&gt;。有办法吗?

【问题讨论】:

  • 一种选择是使用 Boost.Preprocessor 生成 case 语句。
  • 或者,您可以传递一个 tuple 整数,并使用一些元编程让编译器迭代(=递归)相当于您的手写案例。
  • 是否可以将模拟功能更改为类?喜欢:template &lt;int D&gt; struct simulation { static void apply(); };?甚至struct simulation { template &lt;int D&gt; static void apply(); };?
  • 为什么不呢?您是在建议,我应该使用抽象类Simul 的继承吗?然后void runSimul(Simul&amp; s, int d)。或者更简单?
  • @styko 方法更简单,看我的回答。

标签: c++ templates


【解决方案1】:

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

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&lt;sim1&gt;(d);runSimulation&lt;sim2&gt;(d);等调用。

【讨论】:

  • 能解释一下这里的原理吗? ...因为在我看来,似乎仍然需要手动编写 switch 语句。
  • @davidhigh 是的,您需要写出 switch 语句 - 但只需一次。问题是关于如何避免为每个 sim1sim2 等编写相同的长 switch 语句 - 上面可以避免这种情况。
  • 好的,正确。我的回答错了,而是减少了单个开关盒。
【解决方案2】:

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

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
}

DEMO

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

如果效率很重要,您还可以使用 魔术开关 达到 O(1) 的效果,请参阅 herehere - 我是它的忠实粉丝。


编辑:此方案可以与@Daniel Frey 的其他答案结合使用,以便为多种模拟类型和任意多个开关情况提供单一功能。

【讨论】:

    【解决方案3】:

    如果 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) 决定调用哪个单个函数。不幸的是,它需要样板包装器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-01
      • 1970-01-01
      相关资源
      最近更新 更多