Использование 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:

Я заявил, что не могу использовать вектор для этого, потому что я не могу использовать вектор для ЭТОТ. Да, я провел сравнительный анализ. В любом случае, этот вопрос не является исследованием "массивов против векторов". Я хочу избежать этого вопроса, имеющего много комментариев и ответов, говорящих мне "просто использовать вектор". Это как подойти к Эдисону и сказать: "просто используй свечу". Хорошее программирование-это исследование того, что возможно, а не просто утверждение того, что известно. Если мы не можем понять это из-за абсолютной невозможности, это одно. Не изучая возможности решения этой проблемы потому что "вектор был бы проще" - это не так.

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

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

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

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

вот в C++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, class 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)...);
  }
};

magic_switch_t<N>{}( f, 3, blah1, blah2, etc ) вызывает f(index_t<3>{}, blah1, blah2, etc).

некоторые компиляторы C++14 будут подавляться расширением variardic pack, содержащим лямбду. Это не важно, вы можете сделать обходной путь, но обходной путь уродлив.

функции C++14 являются необязательными: вы можете реализовать все это в C++11, но опять же, некрасиво.

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

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


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

шаблон-аргумент для не-типа, не-шаблона шаблон-параметр должен быть одним из:

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

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


аргументы шаблона должны быть константами времени компиляции aka "constant expressions" или constexprS для коротких. Так что нет никакого способа сделать это с помощью шаблонов.

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

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


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