【问题标题】:choose correct template specialization at run-time在运行时选择正确的模板特化
【发布时间】:2011-04-11 13:10:07
【问题描述】:

我有

template <int i> struct a { static void f (); };

在代码的不同位置进行了专门化。如何为仅在运行时知道的i 调用正确的a&lt;i&gt;::f

void f (int i) { a<i>::f (); } // won't compile

我不想在一个大的switch 中列出i 的所有可能值。

编辑:

我想到了类似的东西

#include <iostream>

template <int i> struct a { static void f (); };

struct regf {
  typedef void (*F)();
  enum { arrsize = 10 };
  static F v[arrsize];
  template < int i > static int apply (F f) {
    static_assert (i < arrsize, "");
    v[i] = a<i>::f;
    return 0;
  }
};
regf::F regf::v[arrsize];

template <int i> struct reg { static int dummy; };
template <int i> int reg<i>::dummy = regf::apply<i> ();

void f (int i) { return regf::v[i] (); }

#define add(i) \
  template <> struct a<i> : reg<i> { \
    static void f () { std::cout << i << "\n"; } \
  };

add(1)
add(3)
add(5)
add(7)

int main () {
  f (3);
  f (5);
}

但它崩溃了(我错过了强制实例化的东西吗?),我不喜欢那个 dummy 不是static const(并且使用内存),当然arrsize 比必要的大。


实际问题:有一个函数generate (int i) 调用a&lt;i&gt;::generate () 为仅在运行时给出的i 生成类a&lt;i&gt; 的实例。给出了设计(a&lt;i&gt; 类),它们继承自基类,并且可以随时在代码中的任何位置添加更多的 a 特化,但我不想强迫每个人手动更改我的 generate (i) 为很容易忘记。

【问题讨论】:

  • 再次使用预期的解决方案编写代码,但不是实际问题。询问您要解决的问题。 (不是您预期的解决方案中失败的原因)。你真的需要模板吗?为什么?专长?为什么?需要继承吗?为什么?
  • 我无法更改struct a 的设计(它是更大系统的简化部分),但我需要一个低内存f 而无需手动更改switch添加的每个专业化。

标签: c++ templates template-specialization


【解决方案1】:

我不确定这是否是您可以获得的最佳解决方案,因为可能会有更好的设计,但无论如何您都可以使用一些元编程来触发函数的实例化和注册:

// in a single cpp file
namespace {
template <unsigned int N>
int register_a() {         // return artificially added
   register_a<N-1>();      // Initialize array from 0 to N-1
   regf::v[N] = &a<N>::f;  // and then N
   return N;
}
template <>
int register_a<0>() {
   regf::v[0] = &a<0>::f;  // recursion stop condition
   return 0;
}
const int ignored = register_a<regf::arrsize>(); // call it
}

该代码将实例化函数并注册指向静态成员函数的指针。需要假返回类型才能在静态上下文中强制执行函数(通过使用该函数初始化静态值)。

这很容易导致静态初始化失败。虽然regf::v 没问题,但任何依赖于regf::v 在静态初始化期间包含适当指针的代码都注定会失败。您可以使用通常的技术来改进它...

根据您实际发布的点点滴滴,我猜您正在尝试使用抽象工厂,并从每个具体工厂自动注册。有更好的方法来解决这个问题,但我认为这个答案解决了你的问题(我不确定这是否能解决你的问题)。

【讨论】:

    【解决方案2】:

    你必须这样做。模板在编译时被解析和实例化。除此之外,switch 不一定是低效的。它通常编译成一个查找表,开销很小。

    但是,您可以使用递归模板魔术来嵌套 if/else 块来替换编译器为您生成的 switch。但是一个普通的switch 应该更具可读性。当然,除非你有成千上万的案例。

    在任何一种情况下,您都需要知道i 在编译时可以拥有的一组值,因为编译器需要知道要实例化哪些模板。

    【讨论】:

    • 我正在使用像 add (i, short-code-for-f) 这样的宏来专门处理这个类。有没有比为每个add 更新开关更好的方法? (因为这可能会被遗忘并且容易出错)
    • @Thomas:你真正想解决的问题是什么?根据您的实际需要,您可以采取不同的方法。事实上,您刚刚发布了针对失败的解决方案的初始方法,但在不知道原始问题的情况下很难回答。
    • 在这种情况下,潜在的问题是:为一组由数字索引的类选择正确的构造函数。高效且低内存的解决方案在其他用例中也很有用,我无法重新设计给定的部件。但就我而言,is 从 0 开始并增加。
    【解决方案3】:

    您不能在运行时选择模板特化,它们是根据定义在编译时选择的。

    解决您正在查看的调度问题的常用方法是switch(如您推测的那样)或intvectormap 到函数指针。

    【讨论】:

      【解决方案4】:

      不,编译器需要在编译时实例化模板,因为它需要在编译时知道i的值。

      【讨论】:

        【解决方案5】:

        你不能因为模板实例化是在编译时完成的。

        【讨论】:

          猜你喜欢
          • 2022-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          • 1970-01-01
          • 2019-12-12
          • 2018-01-12
          相关资源
          最近更新 更多