Cannot use constant in class array declaration

Pages: 12
Oct 23, 2022 at 11:13pm
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?
Oct 24, 2022 at 12:48am
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 ;

Oct 24, 2022 at 2:19am
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.
Oct 24, 2022 at 3:29am
> 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
Oct 24, 2022 at 9:27am
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).
Oct 24, 2022 at 5:23pm
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?
Oct 24, 2022 at 7:12pm
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 Oct 24, 2022 at 7:13pm
Oct 25, 2022 at 8:05am
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.
Oct 25, 2022 at 8:44am
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!
Oct 25, 2022 at 11:44am
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
Nov 3, 2022 at 1:20am
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?
Nov 3, 2022 at 9:06am
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 Nov 3, 2022 at 9:33am
Nov 3, 2022 at 11:54am
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 Nov 3, 2022 at 12:22pm
Nov 3, 2022 at 3:07pm
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.
Nov 5, 2022 at 4:17pm
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 Nov 5, 2022 at 4:45pm
Nov 5, 2022 at 5:14pm
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 Nov 5, 2022 at 5:18pm
Nov 5, 2022 at 5:23pm
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.
Nov 5, 2022 at 5:38pm
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 Nov 5, 2022 at 5:52pm
Nov 6, 2022 at 3:14am
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.
Nov 6, 2022 at 1:47pm
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