Search code examples
c++arraysclassinstantiationdynamic-arrays

Instantiation of array class of size unknown at compile time


Below is a small script that prints the sums of the numbers 1 to n, for n = 1,...,20.

#include <iostream>
#include <array>

using namespace std;

int test(int n)
{
    array<int,n> myArray;

    for (int iii = 0; iii < n; iii++)
        myArray[iii] = iii+1;

    int nSum;

    for (int iii = 0; iii < n; iii++)
        nSum += myArray[iii];

    return nSum;
}


int main()
{
    for (int n = 1; n <= 20; n++)
        cout << test(n) << endl;

    return 0;
}

Of course, this won't compile:

Main.cpp: In function ‘int test(long unsigned int)’:
Main.cpp:9:13: error: ‘n’ is not a constant expression
  array<int,n> myArray;
         ^
Main.cpp:9:13: note: in template argument for type ‘long unsigned int’ 
Main.cpp:9:22: error: invalid type in declaration before ‘;’ token
  array<int,n> myArray;
                  ^
Main.cpp:11:26: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for (int iii = 0; iii < n; iii++)
                      ^
Main.cpp:12:14: error: invalid types ‘int[int]’ for array subscript
   myArray[iii] = iii+1;
          ^
Main.cpp:16:26: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for (int iii = 0; iii < n; iii++)
                      ^
Main.cpp:17:22: error: invalid types ‘int[int]’ for array subscript
   nSum += myArray[iii];
                  ^
make: *** [Main.o] Error 1

The problem seems to be (among other things) that n isn't a constant expression, and I get why that is a problem, but I have no idea how to solve it.

I know how to instantiate "regular" arrays with the new function, but I don't understand how it is possible with the array class.

How do I make the test function treat n as a constant?


Solution

  • You have basically two options.

    1. As you already mentioned, std::array needs a compile-time size. So make it known at compile-time! This can be done by converting it to a template parameter.

      Change

      int test(int n)
      {
        .
        .
        .
      }
      

      to

      template<int n>
      int test()
      {
        .
        .
        .
      }
      

      The problem is that it then has to be called with test<n>() instead of test(n) and this again requires the n in the caller code to be known at compile-time, too, which is not the case with a for-loop. If you know your loop's upper bound during compile-time (like in your current code snippet), it can be done, but it's a bit complicated. And if it's not known, i.e. a user input, you can't use this solution at all.

      So this is not a good option here.

    2. Don't use std::array but a container type which doesn't require the size at compile-time but during runtime. A heap-allocated raw array (new int[n]), as you mentioned, is an option, but it's not very C++-ish. Use std::vector instead!

      For this, simply change

      array<int,n> myArray;
      

      to

      vector<int> myArray(n);
      

      PS. In the future, there probably will be std::dynarray (proposed container type) which fits exactly that purpose a bit better than std::vector currently does. It's reflecting the purpose of new int[n] where n is known at runtime but constant during the lifetime of the array, filling the gap between std::array and std::vector. Yet in almost all cases std::vector will do just fine!