Проблема здесь в том, что если вы храните виджеты одного и того же типа, то код, который извлекает виджеты из этого хранилища (вызывая GetWidget) не не знает N во время компиляции [*] , Код, который вызывает конструктор, знает N, но код, который использует объект, должен справиться с несколькими возможностями.
Поскольку снижение производительности (если таковое имеется), скорее всего, происходит в коде, который использует виджеты, а не в коде, который их создает, вы не можете избежать что-то в критическом коде, который зависит на информацию о времени выполнения.
Может быть, что виртуальный вызов функции, реализованной в шаблоне вашего класса, быстрее, чем не виртуальный вызов функции, которая использует N, не зная значения:
class Widget {
public:
virtual ~Widget() {}
virtual void function() = 0;
};
template <int N>
class WidgetImpl : public Widget {
public:
virtual void function() { use N; }
};
Оптимизатор, вероятно, может выполнять свою работу лучше всего, когда известно N, поскольку он может оптимально развернуть циклы, преобразовать арифметику и т. Д. Но с виртуальным вызовом вы начинаете видеть один большой недостаток, заключающийся в том, что ни один из вызовов не может быть встроенным (и я предполагаю, что виртуальный вызов будет менее вероятным, чем не виртуальный, если он не встроен). ). Выигрыш от встраивания с неизвестным N может быть больше, чем выигрыш от знания N, или может быть меньше. Попробуйте их обоих и посмотрите.
Для более надуманных усилий, если имеется достаточно небольшое количество распространенных случаев, вы можете даже увидеть улучшение, реализовав функцию критического виджета, например:
switch(n) {
case 1: /* do something using 1 */; break;
case 2: /* do the same thing using 2 */; break;
default: /* do the same thing using n */; break;
};
«делать что-то» для всех случаев, но по умолчанию это может быть вызов функции, настроенной на константу, тогда по умолчанию используется тот же код с параметром функции вместо параметра шаблона. Или все это могут быть вызовы одной и той же функции (с параметром функции), но с помощью компилятора для встроенного вызова перед оптимизацией в тех случаях, когда параметр является постоянным, для того же результата, как если бы он был шаблонизирован.
Не поддерживается в массовом порядке, и, как правило, плохая идея угадать оптимизатор, подобный этому, но, возможно, вы знаете, каковы общие случаи, а компилятор - нет.
[*] Если вызывающий код знает значение N во время компиляции, тогда вы можете заменить GetWidget
на шаблон функции, подобный этому:
template <int N>
Widget<N> &getWidget(int index) {
return static_cast<Widget<N> &>(whatever you have already);
}
Но я предполагаю, что звонящий не знает, потому что, если бы он знал, вы, вероятно, не спросили бы ...