Using int as a template parameter that is unknown before runtime

I am trying to use an integer as a template parameter for a class. Here is a sample code:

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

If I do something like this, this works:

 sample_class< 10 > sample_class_instance; 

However, suppose I don’t know the value of array_qty (template parameter) at compilation and I will know it only at runtime. In this case, I would essentially pass the int variable as a template argument. For demonstration, the following code does not work:

 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; 

I get the following error during compilation when trying above:

 the value of 'test_array_qty is not usable in a constant expression 

I tried to convert test_array_qty to const, passing it as a template parameter, but that doesn't seem to help either. Is there a way to do this, or am I using the template options incorrectly? Perhaps they need to be known at compile time?

The goal is not to solve this particular approach, but to find a way to set the length of the array to an int variable, which can be specified when creating an instance of the class. If there is a way to do this through a template parameter, that would be ideal.

Note that for this I need to use an array, NOT a vector, which I can use as a suggestion. In addition, array_qty will always have a value from 0 to 50 - in case it matters.

+8
source share
5 answers

It can be done. But believe me when I say that you are asking the wrong question. So, what follows to your question, even the thought that this is a bad idea, almost always.

What you can actually do is create 50 different programs, one for each of the 50 possible sizes, and then conditionally switch to the one you want.

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

Call switcher<50>::run( value ); , and if the value is 0-50, prog<value>::run() called. Within prog::run the template parameter is a compile-time value.

Awful hack, and most likely you are better off using a different solution, but this is what you asked for.

The following is a tabular version of 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 ) will call f(index_t<3>{}, blah1, blah2, etc) .

Some C ++ 14 compilers will strangle a variational package extension containing lambda. It doesn't matter, you can make a workaround, but the workaround is ugly.

The features of C ++ 14 are all optional: you can implement all of this in C ++ 11, but again, ugly.

The passed f should basically be the object of the function (either a lambda that takes auto as the first argument, or manual). Passing a function name directly will not work, because it works best when the first argument becomes a compile-time value.

You can wrap a function template with a lambda or function object.

+11
source

For C ++ 11, non-piggy type template arguments are limited to the following (Β§14.3.2 / 1):

The template argument for a non-piggy template without template must be one of the following:

  • for a non-standard template parameter of an integral or enumerated type, a transformed constant expression (5.19) of the type of a template parameter; or
  • asymmetric pattern name; or
  • constant expression (5.19), which denotes the address of an object with a static storage duration and external or internal communication or a function with external or internal communication, including function templates and identifiers of function templates, but excluding non-static class members, expressed (ignoring brackets) as well as id- an expression, except that it can be omitted if the name refers to a function or array and should be omitted if the corresponding parameter template is a link; or
  • constant expression that evaluates the value of the null pointer (4.10); or
  • constant expression that evaluates the value of the pointer of the zero element (4.11); or
  • pointer to an element expressed as described in 5.3.1.

In C ++ 98 and 03, the list is even more limited. Bottom line: what you are trying to do simply is not allowed.

+5
source

The template arguments must be compilation time constants aka "constant expressions" or constexpr for short. Thus, there is no way to use patterns.

You can use an array of dynamic size and save its size in int .

Or just use vector . Be sure to initialize its size in the constructor, passing the required size to the vector constructor!

+3
source

Sorry, this is not possible. The template argument must be a constant expression known at compile time.

+2
source

I'm a little late, but here is my suggestion. I assume that the main problem with vectors for you is that they allocate more capacity than you need to support dynamic growth. So you can not write your own simple array class?

 template <typename T> class Array { private: T* data; unsigned size; public: Array(unsigned size) { data = new T[size]; this->size = size; } T& operator[](int i) { return data[i]; } T operator[](int i) const { return data[i]; } // Depending on your needs, maybe add copy constructor and assignment operator here. ... unsigned size() { return size; } ~Array() { delete [] data; } } 

From what I know, I believe that this should be as fast as the STL array class. In addition, you can create arrays with unknown sizes until runtime, the memory of the array is processed automatically when it is destroyed, and you do not need to instantiate a new class every time you create a new array with a different size (for example, you have to do for arrays STL).

0
source

All Articles