Why does this variable need to be static? - c++

class armon
{
static const int maxSize=10;
int array[maxSize];
int count=0;
int* topOfStack=array;
}
Why does maxSize need to be static for it to be used inside array?

It doesn't have to be static, but it must be a constant expression.
C++ Standard § 8.3.4 [dcl.array] (emphasis mine) :
If the constant-expression (5.19) is present, it shall be a converted constant expression of type std::size_t and its value shall be greater than zero
That is, the following is also valid :
constexpr std::size_t Size() { return 10; };
struct Y
{
int array[Size()];
};
Note:
Since the compiler needs to know the size of the class, you cannot do this :
struct Y
{
const int size = 10;
int array[size];
};
Possibly making different instances of Y having different sizes.
Also note that in this context, int array[size] is not a constant expression, because it makes use of this, see the C++ standard section § 5.19 [expr.const] :
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
— this (5.1.1), except in a constexpr function or a constexpr constructor that is being evaluated as part of e;
(the evaluation of size is really this->size)

There are two aspects to this question
Aspect 1
C++ array is of fixed size, the size of which needs to be known during compile time. If the decision needs to be deferred during runtime, the array expression becomes ill-formed.
Aspect 2
Declaring a member variable as non-static makes it an instance variable, the value of which only exist once the object is instantiated which is done during run-time. A static variable is a class variable, the value of which can be determined during compile time.
Your particular example becomes the classic chicken-egg paradox.
class armon
{
static const int maxSize=10;
int array[maxSize];
}
In order to instantiate your class armon, you need to know its size.
In order to know its size, you need to know the size of individual members. In your particular case, you need to know the size of the array.
In order to know the size of the array, you need to know the value of the dependent variable maxSize.
In order to access the dependent variable maxSize you need to instantiate the class armon.
In order to instantiate your class armon, you need to know its size.
So, your array size dependent variable should be a constant expression, which in your particular case should be a static variable,

It doesn't have to be static, it has to be constant.
When you declare a constant inside a class you will be making a constant for each instance of the class.
Also if your maxSize is just const you would have to intialize it in the constructor initializer list because const maxSize is treated as variable whose value you can't change.
Inside a class const
keyword means "This is a constant during the hole lifetime of this object". Different objects of the same class can have different values for that constant.
But when it is a static constant there will be only one constant for all instances of the class. This means that you have to initialize constants value on the same line where you are defining it.

Related

Branch with if constexpr() on constinit variables? [duplicate]

I ran across this seemingly odd behavior. A const at top level can be used for array size declarations, but not when the const is in a class.
Here's a compiler explorer demo for MSVC, CLANG and GCC all producing an error:
expression did not evaluate to a constant
It's not really a constant if it's not const at the top level?
There's some argument to be made because top level constants can often be stored in read-only memory, while constants that are not at top level cannot. But is this behavior correct?
struct A {
const int i{ 3 };
};
int main()
{
const int ii{ 3 };
A a;
int j[a.i]{}; // C2131: expression did not evaluate to a constant
int k[ii]{};
}
Generally, you can use (meaning perform an lvalue-to-rvalue conversion on) objects with lifetime starting outside a constant expression only if they are variables marked constexpr or their subobjects (plus some other special cases, that I don't think are important here, see [expr.const]/4 for details).
That you can use a const int variable at all in a constant expression is already a very specific exception. Essentially const-qualified integral and enumeration type variables are also usable in constant expressions if you could have added constexpr to them (meaning that their initializer expression is a constant expression).
This exception is there I guess purely for historical reasons, since it had been allowed before constexpr was introduced in C++11.
Note that all of this talks about variables and their subobjects. Non-static data members are specifically not variables and the exception doesn't apply to them. With constexpr this is more obvious by not allowing it on the declaration of a non-static data member in the first place.
The historical rule was never extended to encompass other types that could be marked constexpr, so e.g. const A a; will not help although that would actually cause a to be storable in read-only memory the same way a const int would.
If an object is none of the cases mentioned above, then an lvalue-to-rvalue conversion on it in a constant expression is not allowed, since it is assumed that the value of the object is not determined at compile-time.
Now, in theory the compiler could still do some constant folding and determine that even other objects' values are definitively known at compile-time. But I think the intention is that whether or not an expression is a constant expression should be (reasonably) well-defined independently of the implementation and so shouldn't rely on how much analysis the compiler can do.
For example
A a;
A b(a);
is also guaranteed to result in b.i == 3. How far do you want to require a compiler to go back or keep track of evaluations? You would need to make some definitive specification if you want to keep the behavior consistent between compilers. But there is already a simple method to indicate that you want the compiler to keep track of the values. You just have to add constexpr:
constexpr A a;
constexpr A b(a);
Now b.i can be used as array index (whether or not it is const and whether or not it is initialized).
With the current rules, any compiler only needs to evaluate the value of objects at compile-time when it sees a constexpr variable or a const integral/enumeration type variable. For all other variables it doesn't need to keep track of values or backtrack when it sees them used in a context which requires a constant expression.
The additional effect of constexpr implying const on the variable makes sure that its value will also never be changed in a valid program and so the compiler doesn't need worry about updating or invalidating the value after the initial computation either. And whether or not an expression is a constant expression is (mostly) implementation-dependent.
ii is a compile-time constant. Its value is known at compile-time, and cannot be changed at runtime. So, ii can be used for fixed array sizes at compile-time.
A::i is not a compile-time constant. It is a non-static instance member. Its value is not known until runtime. After an A object is constructed and its i member is initialized, the value of i cannot be changed because of the const, but the caller can initialize i with whatever value it wants, eg: A a{123};, and thus different A objects can have different i values. So, i cannot be used for fixed array sizes at compile-time. But, it can be used for dynamic array sizes at runtime, via new[], std::vector, etc.
TL;DR
Your assumption that const always implies compile time constant is incorrect. See examples at the end of this answer for more details on this.
Now the problem in using a.i as the size of an array is that in standard C++, the size of an array must be a compile time constant, but since i is a non-static data member, it requires an object to be used on. In other words, after construction of the class object nonstatic data member i gets initialized, which in turn means that a.i is not a constant expression, hence we get the mentioned error saying:
expression did not evaluate to a constant
To solve this, you can make i be a constexpr static data member, as shown below. This works because using a static data member doesn't require an object instance (and hence no this pointer).
struct A {
constexpr static int i{ 3 };
};
int main()
{
const int ii{ 3 };
A a;
int j[a.i]{}; //Correct now and works in all compilers
int k[ii]{};
}
I just don't get why a regular const works in some places but not others.
Perhaps you assuming that const implies compile time constant which is a wrong assumption. An example might help you understand this better:
int i = 10; //i is not a constant expression
const int size = i; //size is not a constant expression as the initializer is not a constant expression
//------vvvv------>error here as expected since size is not a constant expression
int arr[size]{};
On the other hand if you were to make i const as shown below, the program will work fine.
const int i = 10; //note the const added here so that now i is a constant expression
const int size = i; //size is a constant expression as the initializer is a constant expression
//------vvvv------>NO ERROR HERE as expected since size is a constant expression
int arr[size]{};

Enumeration values versus static constants in metaprogramming

I'm reading C++ Templates: The Complete Guide, chapter 23. Metaprogramming. At the end, it describes
the difference in using enumeration values versus static constants in metaprogramming. Consider the following two implementations of calculating Nth power of 3:
Enumeration implementation:
// primary template to compute 3 to the Nth
template<int N>
struct Pow3 {
enum { value = 3 * Pow3<N-1>::value };
};
// full specialization to end the recursion
template<>
struct Pow3<0> {
enum { value = 1 };
};
Static constant implementation:
// primary template to compute 3 to the Nth
template<int N>
struct Pow3 {
static int const value = 3 * Pow3<N-1>::value;
};
// full specialization to end the recursion
template<>
struct Pow3<0> {
static int const value = 1;
};
Right after that it says there is a drawback to the latter version. If we got a function
void foo(int const&); and pass it the result of metaprogram: foo(Pow3<7>::value); the book says:
A compiler must pass the address of Pow3<7>::value, and that forces the
compiler to instantiate and allocate the definition for the static member. As a result,
the computation is no longer limited to a pure “compile-time” effect. Enumeration values aren’t lvalues (i.e., they don’t have an address). So, when we
pass them by reference, no static memory is used. It’s almost exactly as if you passed
the computed value as a literal.
I don't understand their explanation at all to be honest. I thought in the static constant version we evaluate the result at compile time, but the actual referencing happen in run-time because we need to pass the adress. But then I don't see much difference in the former case (enum implementation) because we should have temporary materialization there (prvalue -> xvalue conversion) and since temporary objects also have the adress my thinking process fails. Also it says "no static memory is used" which I don't understand as well since static memory should refer to allocation that happen at compile-time.
Could someone explain this whole thing in greater detail?
(enum implementation) because we should have temporary materialization there (prvalue -> xvalue conversion) and since temporary objects also have the address
they do, but the temporary xvalue only exists for the duration of the function call. It's identical to passing the function an integer literal. So, an addressable object exists temporarily, at runtime, with automatic scope.
Conversely,
... that forces the compiler to instantiate and allocate the definition for the static member. As a result, the computation is no longer limited to a pure “compile-time” effect.
This static const int is an object with static storage duration. There's no temporary materialization necessary, it's an object, it existed when your program started, and you're taking a reference to it.
If you write multiple .cpp files including the header with Pow3, and they all call foo(Pow3<3>):
the enum version will emit something like foo(27) in each translation unit, with three unrelated temporary xvalues
the static version will emit a global object equivalent to const int Pow3<3>::value = 27; and each translation unit will refer to (ie, take a reference to) the same object
I suggest looking at it this way:
There is a difference between a type, an instance of a type, and the values said type can take.
In the first case you are specifying a type (an unnamed enum) with only one possible value value (which is the compile-time constant).
There are no instantiations of the type, so no memory will be used compile-time or runtime.
Every time the code refers to Pow3<N>::value, the compiler will not create an instance of the type but will instead use the constant directly.
In the second case you are instead specifying a variable value of type int, and assigning the compile-time constant to it.
This variable exists, so memory will be used.
Every time the code refers to Pow3<N>::value, the compiler will use said variable.

I can define the length of an array using a constant, so why doesn't int d[b] work?

int a = 5;
const int b = a, c = 4;
int e[a];
int d[b];
int f[c];
The definition of f[c] is valid.
The variable b is also a constant int, but the compiler gave me the error "expression must have a constant value" for the line int d[b]. What are the differences between b and c?
what are the differences between b and c?
c has a compile time constant initialiser, while b does not. A const object with compile time constant initialiser is itself a compile time constant value.
Since I can define an lenth of an arry using a constant ,so why don't this work?
Not just any constant will do. const qualifier implies runtime constness (i.e the value may be determined at runtime but won't change throughout the lifetime of the object). Only compile time constant values can be used as array size.
You are using a non-constant variable to assign value to a constant. Therefore, that variable's value can't be determined compile time. I know you aren't changing a, but the compiler does not think like this.
The compiler diagnostic should really be compile time evaluable constant expression.
Since the original object to which b is assigned is not const, b is not a compile time evaluable constant expression, so compilation will fail as variable length arrays are not supported in standard C++.
The "const" just only means that the variable will not change at run time, and does not mean that its value can be deduced at compiling time.
I guess what you are looking for is "constexpr".
Maybe you can try it out like this:
constexpr int b = 4; // can't be assigned from a!
int d[b];
The "constexpr" instead means a "real const" and the "const" only means "non-changing var", because some historical reason.
It must be mind that a native array in c++ always is fixed-length as #Bathsheba said.
The term "constant" is really ambiguous, and the keyword const is misleading.
const means: "don't allow this object's value won't be changed after initialisation". It does not mean "compile-time constant". Its initial value can (and usually does) still come from runtime sources.
You need a compile-time constant for an array bound.
It is sometimes possible to have an object named foo that is compile-time constant, if it is const and it was initialised from a constant expression, like a literal or a constexpr thing. That's the case for c; it is not the case for b.

C++ Zero Length Arrays in Header File

From ISO/IEC 14882:2003 8.3.4/1:
If the constant-expression (5.19) is present, it shall be an integral
constant expression and its value shall be greater than zero.
Therefore the following should not compile:
#pragma once
class IAmAClass
{
public:
IAmAClass();
~IAmAClass();
private:
int somearray[0]; // Zero sized array
};
But it does. However, the following:
#pragma once
class IAmAClass
{
public:
IAmAClass();
~IAmAClass();
private:
int somearray[0];
int var = 23; // Added this expression
};
does not compile, with the following error (as what would be expected) (Visual C++)
error C2229: class 'IAmAClass' has an illegal zero-sized array
When the code is in a function, it, in accordance with the standard, will never compile.
So, why does the code behave in such a way in a header file, where the difference of the compilation passing or failing appears to be down to whether a statement proceeds the zero sized array declaration or not.
The keyword in "If the constant-expression (5.19) is present," is if. It's not, so the first version compiles.
However, such variant arrays are only permissible (and sane) when they are the last element in a struct or class, where it's expected that they'll use extra space allocated to the struct on a case-by-case basis.
If an unknown-length array were allowed before other elements, how would other code know where in memory to find those elements?
This is a Visual C++ language extension: Declaring Unsized Arrays in Member Lists. From the linked MSDN page:
Unsized arrays can be declared as the last data member in class member lists if the program is not compiled with the ANSI-compatibility option (/Za)
Edit: If the member has been declared as a zero-sized array (like int somearray[0];) instead of an array of unknown bounds (like int somearray[];), this is still a language extension, albeit a different one
A zero-sized array is legal only when the array is the last field in a struct or union and when the Microsoft extensions (/Ze) are enabled.
This extension is similar to C99's flexible array members C11/n1570 §6.7.2.1/18
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.
and /20 contains an example:
EXAMPLE 2 After the declaration:
struct s { int n; double d[]; };
the structure struct s has a flexible array member d. A typical
way to use this is:
int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
and assuming that the call to malloc succeeds, the object pointed to
by p behaves, for most purposes, as if p had been declared as:
struct { int n; double d[m]; } *p;
[...]

At what point does the memory get allocated to a constant variable in c++?

As much as i know, constant class members must be initialized before the constructor runs, but since they cannot be initialized in the class body (as it is just a prototype), therefore we need to initialize it inside initializer list. My question is when does memory gets allocated to a constant variable, and what is the order of execution?
class constant
{
const int a;
public:
constant(int k):a(k)
{
cout<<"a is "<<a<<endl;
}
};
int main()
{
constant cl(5);
return 0;
}
EDIT: Is it true that constant variables need to be initialized at the point where they are allocated memory?
I think you have the wrong idea about const. Think less of it as related to implementation details (like memory), or runtime, and more there as a means to help the programmer and for the compiler.
It doesn't matter when the memory gets allocated (although it's before you construct the object, before entering the initializer list - not specified by the standard), what matters is you can only initialize the variable in the intializer list (pre C++11) or even the class definition for const integral types.
when does memory gets allocated to a constant variable
Here, a is a data member of class constant, so it's allocated as part of constant. Whenever you create an instance of constant, there's an a already included.
Note that static members are different, but just because a isn't allowed to change after initialization, doesn't make its storage different from any other regular data member.
... is it necessary that constant variables be initialized at the point where they are allocated memory
Strictly, you have to have the memory available before you can call the constructor, so the phrase at the point where is a bit problematic (see specifically André Caron's comment about placement new).
However, allocation and construction are tied together in most normal use, and initialization of a const member must happen when the object is constructed.
If the variable is const, the compiler enforces you to not change that value after initialization. That is, you must initialize it (must in the sense of RFC2119).
You must directly initialize it:
struct constant {
const int a;
constant(int k) : a(k) {
/* everything is fine here */
}
};
You must not leave it uninitialized:
struct constant {
const int a;
constant(int k) {
/* error: uninitialized member ‘constant::a’ with ‘const’ type ‘const int’ */
}
};
And you must not change it's value after construction:
struct constant {
const int a;
constant(int k) {
a = k;
/* error: uninitialized member ‘constant::a’ with ‘const’ type ‘const int’ */
/* error: assignment of read-only data-member ‘constant::a’ */
}
};
Exact memory place for object members depends from object creation.
If you create object by "new" it is would be a heap.
If you create stack object (like on your example) it is would be a stack memory.
"Constant" memory - it is memory for "constant", not for "const variables".
Other words, const memory used for literal strings, literal numbers ("text", 5), while a const modifier restrict the memory update.