Cannot use constant in class array declaration

Pages: 12
Windows 11, Visual Studio 2019, C++
In the private section of a class I want an array of a specific size using a named constant.

1
2
3
const size_t max_length = 257;
char  input_ascii[ max_length ];  // error
char input_ascii_2[ 257 ];        // ok 


The second line solicits the error:
1>E:\WX\wx_numbers_01\base_58_verify_class.h(69,37): error C2327: 'base_58_verify_class::max_length': is not a type name, static, or enumerator (compiling source file top_panel.cpp)

Why is a simple constant forbidden in a simple array declaration?
1
2
3
4
private:
   static constexpr std::size_t max_length = 257 ; // note: static
   char input_ascii[max_length] {} ;
   std::size_t curr_length = 0 ;

Size_t is a narrow name for an unsigned int. “const unsigned int” tells the compiler, where an unsigned is appropriate and the name of the constant is found, put the value there. The scope is defined by the limitations of the class in which it is declared. It should only be valid within the scope of the class and when an object of the class is instantiated. There is no need for the constant to be valid outside of the scope of the and/or when no object of said class is instantiated.

So why does this need to be static?

And it is not an expression. The number 257, or another other specific number, not calculated, is not an expression. It is just a number.

And even if calculated such as:
Const int x_size = 10; const int y_size = 20; const area = x_size * y_size;

It can and should be calculated at compile time and never again. “constexpr” is not needed.
> So why does this need to be static?

The array bound must be a (converted) constant expression of type std​::​size_­t.

Constant expression: https://en.cppreference.com/w/cpp/language/constant_expression

It can be a non-static member of a constexpr object; for example:
1
2
3
4
5
6
7
struct C { std::size_t max_length = 257 ; };
constexpr C cc ;

struct A
{
   char input_ascii[ cc.max_length ] {} ;
};



> And it is not an expression. The number 257, or another other specific number, not calculated,
> is not an expression. It is just a number.

257 is a A literal; it is a primary expression of type int and value category prvalue

7 Expressions
....7.5 Primary expressions
........7.5.1 Literals
http://eel.is/c++draft/expr.prim.literal
Size_t is a narrow name for an unsigned int.


No. This depends upon whether compile as 32 or 64 bits. For 64 bit compile will be unsigned long long (64 bits).
Time to change my perspective. A question about this declaration
1
2
3
4
5
6
7
8
class my_class
{
private:
const size_t max_1 = 256;
char  str_1[  max_length ];  // error
static const size_t max_2 = 256
char str_2[ max_2 ];             // this is ok
}

Question: What advantage is accrued by adding the word static?
But something else is strange. I noticed that in an h file, not within a class, are these lines:
1
2
3
4
5
6
7
8
9
10
const size_t test_array_count = 5;
const size_t test_array_max_length = 50;
const char base_58_text[ test_array_count ][ test_array_max_length ] =
{
   "1",
   "A",
   "alpha",
   "Hello World",
   "Works well for a long string."
};

The phrase static is not there, but the compiler does not complain.
This strikes me as being inconsistent. What am I missing?
The test_array_count is a singleton; one instance in the entire program. Initialized exactly once.

The my_class::max2 is a singleton; one instance in the entire program. Initialized exactly once.


Each instance of my_class will have its own max_1.

Tell what happens here:
1
2
3
4
5
6
7
8
9
10
11
12
class my_class
{
public:
  my_class( size_t m ) : max_1{m} {}
private:
  const size_t max_1 = 256;
};

int main() {
  my_class answer( 42 );
  my_class lucky( 7 );
}

Are answer.max_1 and lucky.max_1 constants? Yes they are.
Are they 256? Oh no.
Last edited on
This strikes me as being inconsistent. What am I missing?
The difference between member variable and global variable.

An array size requires a global constant that exists throughout the lifetime of the program. A member variable does not fulfill this and hence the array size could not be determined at compile time.
Amazing as it may seem, a member const variable can have it's value set at run-time. As the size of an array has to be be known at compile time, a const member variable can't be used for an array size!
The only requirement is that the array bound (the number of elements in the array) must be a constant expression;
and its value should be greater than zero.

Anything is allowed, as long as this requirement is met.
(Constant expressions are expressions that can be evaluated at compile time. http://eel.is/c++draft/expr.const )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

struct A { int sz = 57 ; /* non-static, non-const */ };
constexpr A ga ;

constexpr int input_size() noexcept
{
    constexpr A la { 150 } ;
    const int ci = 50 ;
    return ga.sz + la.sz + ci ;
}

struct B
{
    char input_ascii[ input_size() ] {} ;
};

int main()
{
    static_assert( input_size() == 257 ) ;
    const B b { "Hello World!" } ;
    std::cout << b.input_ascii << '\n' ;
}

http://coliru.stacked-crooked.com/a/dd78e521ebe2033c
The answers have covered too much ground and I am not able to recognize the essence of my question. Presume an h file contains this:

class one
{
const int name_max = 64;
char name[ name_max ];
// more class code.
};


When an object of this class is created the name array is set to 64 characters. If another object of this class is created, or N objects created, each one has a char array "name" that is 64 characters is size.

How can this cause a problem?
name_max is not a compile-time constant. Each object would have its own name_max variable. 64 is just the default value.

Constructors could initialize it to a different value.
1
2
3
4
5
6
7
class one
{
private:
	const int name_max = 64;
public:
	one(int n) : name_max(n) {}
};

Or if one is an "aggregate class" (only public member variables, no user-provided constructors, etc.) then you could specify a value when initializing the object.
1
2
3
4
5
6
7
8
class one
{
public:
	const int name_max = 64;
};

one x; // x.name_max is equal to 64
one y{20}; // y.name_max is equal to 20 

So that is why name_max is not considered to be a compile-time constant.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const unsigned name_max1 { 64 };
constexpr unsigned name_max2 { 64 };

struct one {
	const unsigned name_max { 64 };
	//constexpr unsigned name_max6 { 64 };      // NO. constexpr not allowed here
	static const unsigned name_max3 { 64 };
	static inline unsigned name_max4 { 64 };
	static inline const unsigned name_max5 { 64 };
	//inline const unsigned name_max7 { 64 };   // NO. inline not allowed here

	//char name[name_max];  // Error - not a compile time constant
	char name1[name_max1];  // OK
	char name2[name_max2];  // OK
	char name3[name_max3];  // OK
	//char name4[name_max4];  // Error - not a compile time constant
	char name5[name_max5];  // OK
};

Last edited on
Windows 11, Visual Studio 2019, C++

Consider replacing VS 2019 with VS 2022, or if you have the space both can be installed side-by-side. VS 2022 requires a 64-bit CPU to run, though it can still compile for 32-bit.

Not that it matters in this instance 2022 is more likely to be updated for C++23.

VS 2019 is fully C++20 as is 2022 if that is of any concern.

VS 2022 gobbles up less HD space for the same installed packages, is a bit less CPU intensive and marginally less buggy. In my experience 2019's C++20 implementation, especially using custom created modules, can have compile-time issues. Intellisense can really goes loopy in 2019.
Peter87 wrote:
name_max is not a compile-time constant. Each object would have its own name_max variable. 64 is just the default value.


I cannot see it that way. The declaration is:
const int name_max = 64;

That says: Compiler! Every time you see the variable named name_max, it SHALL have the value 64! Do not ever allow name_max to contain or represent ANY other value.

Peter87: example code instantiated a class with the line:
one y{20}

I do not see how that changed the value of the constant. The constructor does not have an argument. I do not see anything that directs the value 20 to the declared constant name_max.

GeorgeP: You pushed me over the edge, purchase of VS 2022 is in progress.
Last edited on
I cannot see it that way. The declaration is:
const int name_max = 64;
That says: Compiler! Every time you see the variable named name_max, it SHALL have the value 64! Do not ever allow name_max to contain or represent ANY other value.

That's what it means if you write it inside a function or in global/namespace scope but not if you write it as a non-static member of a class. Context matters.

example code instantiated a class with the line:
one y{20}
I do not see how that changed the value of the constant. The constructor does not have an argument. I do not see anything that directs the value 20 to the declared constant name_max.

Do you know what a struct is? Then you perhaps know that you can specify the values for the member variables this way. If you do not declare a constructor and make all member variables public then you can do the same with classes.

It's called aggregate initialization.
https://en.cppreference.com/w/cpp/language/aggregate_initialization

Note that the value of the const member variable didn't change. It was simply initialized to that value when it was created.

Technically structs and classes are the same thing. The only difference is that struct use public by default while class use private by default. Everything you can do with a struct you can do with a class and vice versa.
Last edited on
That says: Compiler! Every time you see the variable named name_max, it SHALL have the value 64! Do not ever allow name_max to contain or represent ANY other value.


NO!! Not as a variable definition within a class. To have that meaning you also need to define as static. That's the way the C++ language is defined. Accept what the language means and move on. You've been told this several times now.
It's consistent with how it works for non-const member variables.

1
2
3
4
5
6
7
8
9
struct Foo
{
    int x = 5;
    const int y = 5;
};

Foo f1;       // f1.x == 5 && f1.y == 5
Foo f2{2};    // f2.x == 2 && f2.y == 5
Foo f3{2, 3}; // f3.x == 2 && f3.y == 3 

In this example the value 5 is just the default that the member variables will be initialized to if you do not specify a different value.

Take f2.x as an example. It was never given the default value 5. Instead it was initialized to 2 from the start.

const just means you're not allowed to modify the variable, but that's not what we're doing here.
Last edited on
Regarding VS 2022, BitDefender declares the msbuild.exe a threat and won't let it run. Found a thread about that a few months old and the problem has not been corrected. Back to VS 2019.
Have you ever considered it is BitDefender that is at fault with a false issue?

You can add exceptions to BitDefender.

How to exclude files and folders from Bitdefender Antivirus scan - https://www.bitdefender.com/consumer/support/answer/13427/
Pages: 12