1

I try to use a global variable as elements for an array. The problem is that the compiler want to know the integer constant value while declaring an array. I thought that my global variable is allready a const int???

global header file "constants.h"

#ifndef CONSTANT_H
#define CONSTANT_H

namespace constants{
    extern const int MY_ROW;
    extern const int MY_COLUMN;
}
#endif // CONSTANT_H

define global "constants.cpp"

#include "constants.h"

namespace constants{
    const int MY_ROW{55};
}

main function "main.cpp"

#include "constants.h"
#include <iostream>
#include <array>

int main()
{
    std::cout<<constants::MY_ROW<<std::endl;
    int my_int_array[constants::MY_ROW];
    return 0;
}

So far everything is going well, I can declare an array in main using globals as elements.

But if I try "the same thing" in another header the compiler complains.

"test.h"

#ifndef TEST_H
#define TEST_H
#include "constants.h"

class Test
{
public:
    Test();
    ~Test();
    void display_array();
private:
    int test_array[constants::MY_ROW];    //error here???
};
#endif // TEST_H

error message:

**error: array bound is not an integer constant before ']' token|**

I appreciate it if someone can bring light to darkness.

Module_art
  • 999
  • 2
  • 9
  • 26

2 Answers2

1

In main(), for int my_int_array[constants::MY_ROW]; the value of MY_ROW is not known to the compiler, as it is not resolved until the linker stage, so my_int_array cannot be allocated at compile-time, only at runtime, and only if you are using a compiler that supports Variable Length Arrays (see Why aren't variable-length arrays part of the C++ standard?).

But for class Test, that is simply not an option. You can't use a VLA in a class, since the compiler needs to know up front the full size of all members in order to setup memory storage for instances of the class. But the size of the array is not know to the compiler, so the class cannot be compiled.

To do what you are attempting, you need to initialize the constants directly in their declarations, not split them up with extern, eg:

#ifndef CONSTANT_H
#define CONSTANT_H

namespace constants{
    const int MY_ROW = 55;
    const int MY_COLUMN = ...;
}
#endif // CONSTANT_H

See Define constant variables in C++ header

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • There is no problem in main() with declaring the array with MY_ROW and I said that before the problem occurs in class Test. Why I used extern is because I wanna prevent multiple variables when including in other files. – Module_art Apr 25 '20 at 19:38
  • Your last recommendation would mean that my variable is defined multiple time in each source. The goal is to have only one global variable. – Module_art Apr 25 '20 at 19:48
  • 1
    @Module_art "*There is no problem in main() with declaring the array with MY_ROW*" - I explained why that works in your case (VLA), and why it is non-standard behavior that should be avoided. "*I wanna prevent multiple variables when including in other files.*" - you don't need to avoid that when defining constants. "*Your last recommendation would mean that my variable is defined multiple time in each source*" - which is perfectly fine, and they can be optimized away. When a compile-time constant is used, the compiler is free to replace the variable with the actual value at the site of usage. – Remy Lebeau Apr 25 '20 at 19:59
  • @Module_art _Why I used extern is because I wanna prevent multiple variables when including in other files._ Pre-C++17, you can either have a value be known at compile time (which is required to declare a non-VLA array), or have it be given external linkage, not both. In C++17, you can use inline variables to get both, which seems to be precisely what you’re trying to do here. – Brian61354270 Apr 25 '20 at 22:10
1

As noted by Remy, the issue is that the compiler can't see the value of MY_ROW when main.cpp or any file #includeing test.h is compiled.

If you're using C++17, there's a fairly elegant fix for this. The goal is to make the value MY_ROW visible when main.cpp is compiled, so we need to move the definition of MY_ROW to constants.h:

#ifndef CONSTANT_H
#define CONSTANT_H

namespace constants{
    constexpr int MY_ROW{55};
    // ...
}
#endif // CONSTANT_H

Note that I swapped const with constexpr to ensure that MY_ROW is a compile time constant rather than a runtime constant.

This version of constants.h will allow your program to compile, but its still not ideal. The issue is that every file that includes constants.h will receive its own MY_ROW definition with static linkage. This means that we may end up with multiple MY_ROWS being stored in the final executable, all with different addresses.

Luckily, in C++17, inline variables were introduced, which essentially let us provide multiple definitions for a single variable with external linkage:

#ifndef CONSTANT_H
#define CONSTANT_H

namespace constants{
    inline constexpr int MY_ROW{55};
    // ...
}
#endif // CONSTANT_H

With this implementation, you are free to include constants.h in multiple translation units and the linker will ensure that only one MY_ROW makes it to the final executable. You are also now free to use MY_ROW wherever a compile time constant is needed, so your declarations of my_int_array in main.cpp and test_array in test.h would now be valid.

Brian61354270
  • 8,690
  • 4
  • 21
  • 43
  • The question/problem was not based on C++17. Your first sentance isn't true because it can compile and I don't know why you came up with this??? – Module_art Apr 25 '20 at 19:52
  • @Module_art _Your first sentance isn't true because it can compile“_ I don’t follow what you mean. Your question is why your code doesn’t compile, and the reason is precisely what both I and Remy discuss. If you want to declare compile time constatants in a header, this is how you may do so. Is that not what you are looking for? – Brian61354270 Apr 25 '20 at 22:03