выберите правильную специализацию шаблона во время выполнения


У меня есть

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

С специализациями, выполненными в разных местах кода. Как я могу вызвать правильный a<i>::f для i, известного только во время выполнения?

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

Я не хочу перечислять все возможные значения i в большом switch.

Правка:

Я подумал о чем-то вроде

#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);
}

Но он падает (я что-то пропустил, чтобы заставить экземпляр?), и мне не нравится, что манекен не static const (и использует память) и, конечно, что arrsize больше, чем необходимый.


Актуальная задача: иметь функцию generate (int i), которая вызывает a<i>::generate () для создания экземпляра класса a<i> для i, заданного только во время выполнения. Дизайн (классы a<i>) задан, они наследуются от базового класса и больше специализаций a могут быть добавлены в любое время в любом месте кода, но я не хочу заставлять всех изменять мой generate (i) вручную, так как это может быть легко забыто.

5 5

5 ответов:

Я не уверен, что это лучшее решение, которое вы можете получить, так как могут быть лучшие проекты, во всяком случае, вы можете использовать некоторое метапрограммирование для запуска экземпляра и реестра функций:

// 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
}

Этот код будет создавать экземпляры функций и регистрировать указатели на статические функции-члены. Тип fake return необходим для принудительного выполнения функции в статическом контексте (посредством использования этой функции для инициализации статического значение).

Это весьма склонно к фиаско статической инициализации. Хотя regf::v в порядке, любой код, который зависит от regf::v, содержащий соответствующие указатели во время статической инициализации, обречен на неудачу. Вы можете улучшить это с помощью обычных методов...

Из кусочков и фрагментов, которые вы на самом деле разместили, я предполагаю, что вы пытаетесь использовать абстрактную фабрику с автоматической регистрацией от каждой конкретной фабрики. Есть лучшие способы приблизиться к цели. проблема, но я думаю, что этот ответ решает ваш вопрос (я не уверен, решает ли это вашу проблему).

Ты должен. Шаблоны разрешаются и создаются во время компиляции. Кроме того, a switch не обязательно должен быть неэффективным. Он обычно компилируется в таблицу поиска с очень небольшими накладными расходами.

Однако вы можете использовать рекурсивную шаблонную магию, чтобы вложенные блоки if/else заменяли блоки switch, созданные для вас компилятором. Но простой switch должен быть гораздо более читабельным. Если, конечно, у вас нет буквально тысяч случаев.

В любом случае вам необходимо знать набор значений это i может иметь место во время компиляции, так как компилятор должен знать, какие шаблоны создавать.

Вы не можете выбрать специализацию шаблона во время выполнения, они по определению выбираются во время компиляции.

Обычные способы решения проблемы диспетчеризации, которые вы рассматриваете, - это switch (как вы предположили) или vector или map из int указателя на функцию.

Нет, компилятор должен создать экземпляр шаблона во время компиляции , для этого ему нужно знать значение i во время компиляции.

Вы не можете, так как создание экземпляра шаблона выполняется во время компиляции.