Использование int в качестве параметра шаблона, который неизвестен до времени выполнения

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

template< int array_qty > 
class sample_class {
 public:
 std::array< std::string, array_qty > sample_array;
}

Если я так делаю, это работает:

sample_class< 10 > sample_class_instance;

Однако скажем, что я не знаю значения array_qty (параметр шаблона) при компиляции и будет знать его только во время выполнения. В этом случае я по существу передавал бы переменную int в качестве аргумента шаблона. Для демонстрации следующий код не работает:

int test_var = 2;
int another_test_var = 5;
int test_array_qty = test_var * another_test_var;
sample_class< test_array_qty > sample_class_instance;

Во время компиляции при попытке вывести следующую ошибку:

the value of ‘test_array_qty’ is not usable in a constant expression

Я пробовал преобразовать test_array_qty в const, передавая его как параметр шаблона, но это тоже не похоже на трюк. Есть ли способ сделать это, или я неправильно использую параметры шаблона? Возможно, их нужно знать во время компиляции?

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

Обратите внимание, что для этого я должен использовать массив, а НЕ вектор, который я могу в итоге предложить. Кроме того, array_qty всегда будет значением от 0 до 50 - в случае, если это имеет значение.

Edit:

Я заявил, что я не могу использовать вектор для этого, потому что я НЕ МОГУ ИСПОЛЬЗОВАТЬ ВЕКТОР ДЛЯ ЭТОГО. Да, я проверил это. В любом случае, этот вопрос не является исследованием "массивов против векторов". Я хочу избежать этого вопроса, имея много комментариев и ответов, говорящих мне "просто использовать вектор". Это похоже на то, чтобы подойти к Эдисону и сказать "просто используйте свечу". Хорошее программирование - это исследование того, что возможно, а не просто утверждение того, что известно. Если мы не сможем понять это из-за чистой невозможности, это одно. Не исследовать возможность решения этого, потому что "вектор был бы проще" - нет.

Кроме того, я не понимаю, почему на этом есть нисходящая линия. Это совершенно правильный вопрос, заданный последовательным образом.

4 ответа

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

То, что вы в действительности можете сделать, это создать 50 различных программ, по одному для каждого из 50 возможных размеров, а затем условно перейти на тот, который вы хотите.

template<int n="">
struct prog {
 void run() {
 // ...
 }
};
template<int n="">
struct switcher {
 void run(int v) {
 if(v==n)
 prog<n>::run();
 else
 switcher<n-1>::run(v);
 }
};
template<>
struct switcher<-1> {
 void run(int v){
 }
};
</n-1></n></int></int>

Вызов switcher<50>::run( value );, и если значение равно 0-50, вызывается prog<value>::run()</value>. В пределах prog::run параметр шаблона является значением времени компиляции.

Ужасный взломать, и, скорее всего, вам лучше использовать другое решение, но это то, о чем вы просили.

Ниже приведена табличная версия С++ 14:

template<size_t n="">
using index_t = std::integral_constant<size_t, n="">; // C++14
template<size_t m="">
struct magic_switch_t {
 template<class...args>
 using R=std::result_of_t<f(index_t<0>, Args...)>;
 template<class f,="" class...args="">
 R<args...> operator()(F&& f, size_t i, Args&&...args)const{
 if (i >= M)
 throw i; // make a better way to return an error
 return invoke(std::make_index_sequence<m>{}, std::forward<f>(f), i, std::forward<args>(args)...);
 }
private:
 template<size_t...is, f,="" class...args="">
 R<args...> invoke(std::index_sequence<is...>, F&&f, size_t i, Args&&...args)const {
 using pF=decltype(std::addressof(f));
 using call_func = R<args...>(*)(pF pf, Args&&...args);
 static const call_func table[M]={
 [](pF pf, Args&&...args)->R<args...>{
 return std::forward<f>(*pf)(index_t<is>{}, std::forward<args>(args)...);
 }...
 };
 return table[i](std::addressof(f), std::forward<args>(args)...);
 }
};
</args></args></is></f></args...></args...></is...></args...></size_t...is,></args></f></m></args...></class></f(index_t<0></class...args></size_t></size_t,></size_t>

magic_switch_t<n>{}( f, 3, blah1, blah2, etc )</n> будет вызывать f(index_t<3>{}, blah1, blah2, etc).

Некоторые компиляторы С++ 14 задушат расширение вариационного пакета, содержащее лямбда. Это не важно, вы можете сделать обходной путь, но обходной путь уродливый.

Возможности С++ 14 все необязательны: вы можете реализовать все это на С++ 11, но опять же, уродливо.

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

Вы можете обернуть шаблон функции с помощью лямбда или функционального объекта.


Для С++ 11 аргументы шаблона непигового типа ограничиваются следующим (§14.3.2/1):

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:

  • для нетипового шаблона-параметра интегрального или перечисляемого типа, преобразованного константного выражения (5.19) типа шаблона-параметра; или
  • имя несимметричного шаблона; или
  • постоянное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженное (игнорирование скобок) как и id-выражение, за исключением того, что и может быть опущено, если имя относится к функции или массиву и должно быть опущено, если соответствующий шаблон-параметр является ссылкой; или
  • константное выражение, которое вычисляет значение нулевого указателя (4.10); или
  • константное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или
  • указатель на элемент, выраженный как описано в 5.3.1.

В С++ 98 и 03 список еще более ограничен. Итог: то, что вы пытаетесь сделать просто, не разрешено.


Аргументы шаблона должны быть константами времени компиляции aka "константные выражения" или constexpr для краткости. Таким образом, нет возможности использовать шаблоны.

Вы можете использовать массив динамического размера и сохранить его размер в int.

Или просто используйте vector. Обязательно инициализируйте его размер в конструкторе, передав требуемый размер векторному конструктору!


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

licensed under cc by-sa 3.0 with attribution.