#include <iostream>
using namespace std;
int main(void){
int size = -2;
int* p = new int[size];
cout<<p<<endl;
return 0;
}
Above code compiles without any problem on visual studio 2010.
But
#include <iostream>
using namespace std;
int main(void){
const int size = -2;
int* p = new int[size];
cout<<p<<endl;
return 0;
}
But this code(added const keyword) gives error on compilation(size of array cannot be negative).
Why these different results ?
By making it a constant expression, you've given the compiler the ability to diagnose the problem at compile time. Since you've made it const, the compiler can easily figure out that the value will necessarily be negative when you pass it to new.
With a non-constant expression, you have the same problem, but it takes more intelligence on the part of the compiler to detect it. Specifically, the compiler has to detect that the value doesn't change between the time you initialize it and the time you pass it to new. With optimization turned on, chances are pretty good at least some compilers still could/would detect the problem, because for optimization purposes they detect data flow so they'd "realize" that in this case the value remains constant, even though you haven't specified it as const.
Because const int size = -2; can be replaced at compilation time by the compiler - whereas a non-const cannot - the compiler can tell that size is negative and doesn't allow the allocation.
There's no way for the compiler to tell whether int* p = new int[size]; is legal or not, if size is not const - size can be altered in the program or by a different thread. A const can't.
You'll get into undefined behavior running the first sample anyway, it's just not legal.
In the first, size is a variable whose value happens to be -2 but the compiler doesn't track it as it is a variable (at least for diagnostic purpose, I'm pretty sure the optimizing phase can track it). Execution should be problematic (I don't know if it is guarantee to have an exception or just UB).
In the second, size is a constant and thus its value is known and verified at compile time.
The first yields a compilation error because the compiler can detect a valid syntax is being violated at compilation time.
The Second yields a Undefined Behaivor[1] because compiler cannot detect the illegal syntax at compilation time.
Rationale:
Once you make the variable a const the compiler knows that the value of the variable size is not supposed to change anytime during the execution of the program. So compiler will apply its optimization & just simply replace the const integral type size with its constant value -2 wherever it is being used at compile time, While doing so it understands that legal syntax is not being folowed[1] and complains with a error.
In the Second case not adding a const prevents the compiler from applying the optimization mentioned above, because it cannot be sure that the variable size will never change during the course of execution of the program.So it cannot detect the illegal syntax at all.However, You end up getting a Undefined Behavior at run-time.
[1]Reference:
C++03 5.3.4 New [expr.new]
noptr-new-declarator:
[ expression ] attribute-specifier-seqopt
noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
constant-expression are further explained in 5.4.3/6 and 5.4.3/7:
Every constant-expression in a noptr-new-declarator shall be an integral constant expression (5.19) and evaluate to a strictly positive value. The expression in a direct-new-declarator shall have integral or enumeration type (3.9.1) with a non-negative value. [Example: if n is a variable of type int, then new float[n][5] is well-formed (because n is the expression of a direct-new-declarator), but new float[5][n] is ill-formed (because n is not a constant-expression). If n is negative, the effect of new float[n][5] is undefined. ]
Related
#include <memory>
int main() {
constexpr auto v = [] {
std::allocator<char> a;
auto x = a.allocate(10);
x[2] = 1;
auto r = x[2];
a.deallocate(x, 10);
return r;
}();
return v;
}
Is the program ill-formed? Clang thinks so, GCC and MSVC don't: https://godbolt.org/z/o3bcbxKWz
Removing the constexpr I think the program is not ill-formed and has well-defined behavior:
By [allocator.members]/5 the call a.allocate(10) starts the lifetime of the char[10] array it allocates storage for.
According to [intro.object]/13 starting the lifetime of an array of type char implicitly creates objects in its storage.
Scalar types such as char are implicit lifetime types. ([basic.types.general]/9
[intro.object]/10 then says that objects of type char are created in the storage of the char[10] array (and their lifetime started) if that can give the program defined behavior.
Without beginning the lifetime of the char object at x[2], the program without constexpr would have undefined behavior due to the write to x[2] outside its lifetime, but the char object can be implicitly created due to the arguments above, making the program behavior well-defined to exit with status 1.
With constexpr, I am wondering if the program is ill-formed or not. Does implicit object creation apply in constant expressions?
According to [intro.object]/10 objects are implicitly created to give the program defined behavior, but does being ill-formed count as defined behavior?
If not, then the program should not be ill-formed because of implicit creation of the char object for x[2].
If yes, then the next question would be if it is unspecified whether the program is ill-formed or not, because [intro.object]/10 also says that it is unspecified which objects are implicitly created if multiple sets can give the program defined behavior.
From a language design perspective I would expect that implicit object creation is not supposed to happen in constant expressions, because verifying the (non-)existence of a set of objects making the constant expression valid is probably infeasible for a compiler in general.
2469. Implicit object creation vs constant expressions
It is not intended that implicit object creation, as described in 6.7.2 [intro.object] paragraph 10, should occur during constant expression evaluation, but there is currently no wording prohibiting it.
Clang is incorrect here. You've already cited all the parts in the spec that make it well-formed. std::allocator<T>::allocate is constexpr; you get a pointer to char*; allocator<T>::allocate creates an array of T; creating an array of char implicitly creates objects; accessing a char attempts to cause UB, but IOC prevents UB, so UB doesn't happen. Therefore: the code isn't allowed to be il-formed.
Clang claims full support for both IOC and constexpr allocators, so this code should work.
Does implicit object creation apply in constant expressions?
All expressions are core constant expressions unless [expr.const]/5 explicitly excludes it. Nothing there mentions operations which might be UB that determines which objects are created, so such operations must be included.
IOC prevents an expression from being UB.
I would expect that implicit object creation is not supposed to happen in constant expressions, because verifying the (non-)existence of a set of objects making the constant expression valid is probably infeasible for a compiler in general.
You have forgotten about the other restrictions on constexpr code. So long as [expr.const]/5 continues to explicitly forbid reinterpret_cast and conversions from void*, the number of ways you can abuse IOC is pretty limited. You cannot, for example, take the pointer returned by your allocate(10) call and convert it to an int*. So the compiler knows that the only objects that can be implicitly created in that storage are chars.
So at constant evaluation time, the compiler could just take the result of allocator<char>::allocate and create all the char members of that array immediately before returning it. There's no constexpr-valid way for you to take that storage and implicitly create anything other than chars.
And using allocator<T>::allocate when T isn't a byte-wise type will not implicitly create objects in that storage. So either you're just getting a pointer to an array of unformed elements, or you're getting a pointer to an array of byte-wise types.
I'd guess Clang forgot to check this particular case.
Simple question:
Why does the code below work?
int main() {
const int a = 4;
const int b = 16;
const int size = b/a;
int arr[size] = {0,1,2,3};
return 0;
}
I thought the size of static arrays have to be defined during compile time, thus only with an "int" literal. In the code above, the code compiles although the size is a calculation. Is this calculation done during compile time?
If yes, maybe my understanding of compile and running is wrong: Compile just goes through the syntax and translates the code to machine code, but does not do any calculations...
Thank you!
Is this calculation done during compile time?
Yes, compilers did a lot optimizations and calculations for you, the initialization in your code is ok even without any optimization, and it's the result of pre-calculation of compiler.
Generally, calculation here includes the constexpr, const type declaration and so on, which are already in the definition of language itself(see constant expression).
compile-time const and example
just see the output of the example.
compile-time constexpr and example
The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.
This is how array can be initialized, an array declaration is as below:
noptr-declarator [ expr(optional) ] attr(optional)
here, expr is:
an integral constant expression (until C++14) a converted constant expression of type std::size_t (since C++14), which evaluates to a value greater than zero
which are all constant expression, which says:
an expression that can be evaluated at compile time.
So, using the so-called pre-calculation to initialize an array is ok.
Here's also a follow-up: there're also a lot of ways to save more calculations and time by let them done during compile time, and they are shown in the link above.
Just to mention something different: As for optimizations, you can see the difference between the assembly codes of version -O0 and -O3 when calculating the sum from 1 to 100, it's a jaw-breaker -- you'll see the result 5050 is in the assembly code in the -O3 version, it's also a kind of compile-time calculation, but not enabled for all kinds of situation.
I thought the size of static arrays have to be defined during compile time, thus only with an "int" literal.
The first part is true, but the second part was only true before c++11 (you could also have done const int i = 1 + 2). From c++11, the rules say that the initializing expression is evaluated to see if it yields a constant expression.
There is also the rule that a const integral type is implicitly constexpr (i.e. computed at compile time), so long as the initializing expression is computable at compile time.
So in this expression:
const int size = b/a;
the variable size is required to be computed at compile time, so long as the expression a/b is a constant expression. Since a and b are also const ints that are initialized with a literal, the initialization of size is a constant expression. (Note that removing const from any of the declarations will make size a non-constant expression).
So size is required to be computed at compile time (the compiler has no choice in the matter, regardless of optimizations), and so it can be used as an array dimension.
Here are the complete rules for how this works.
I'm using godbolt to see generated code with gcc and clang.
I tried to implement to djb2 hash.
gcc always trying is best to eval constexpr function.
clang is evaluating constexpr only if the variable is constexpr.
Let's see the example:
constexpr int djb2(char const *str)
{
int hash = 5381;
int c = 0;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
int main()
{
int i = djb2("hello you :)");
}
With this example, gcc is evaluating a compile time i. But clang at run time.
If I add constexpr to i, clang is evaluating also at compile time.
Do you know if the standard is saying something about that ?
EDIT: thanks to all. So, as I understand, without constexpr the compiler is doing what is want. With constexpr, the compiler is forced to evaluating the constant.
Your program has undefined behavior.
The shift hash << 5 will overflow which has undefined behavior for signed integer types before C++20.
In particular that means that calling your function can never yield a constant expression, which you can verify by adding constexpr to your declaration of i. Both compilers will then have to diagnose the undefined behavior and will tell you about it.
Give hash an unsigned type and your code will actually have well-defined behavior and the expression djb2("hello you :)" will actually be a constant expression that can be evaluated at compile-time, assuming you are using C++14 or later (The loop was not allowed in a constexpr function in C++11.).
This still doesn't require the compiler to actually do the evaluation at compile-time, but then you can force it by adding constexpr to the declaration of i.
"Force" here is relative. Because of the as-if rule and because there is no observable difference between evaluation at compile-time and runtime, the compiler is still not technically required to really do the computation only at compile-time, but it requires the compiler to check the whole calculation for validity, which is basically the same as evaluating it, so it would be unreasonable for the compiler to repeat the evaluation at runtime.
Similarly "can be evaluated at compile-time" is relative as well. Again for the same reasons as above, a compiler can still choose to do the calculations at compile-time even if it is not a constant expression as long as there wouldn't be any observable difference in behavior. This is purely a matter of optimizer quality. In your specific case the program has undefined behavior, so the compilers can choose to do what they want anyway.
I understand that the size of the built-in array must be a constant expression:
// Code 1
constexpr int n = 5;
double arr[n];
I do not understand why the following compiles:
// Code 2
const int n = 5;
double arr[n]; // n is not a constant expression type!
Furthermore, if the compiler is smart enough to see that n is initialized with 5, then why does the following not compile:
// Code 3
int n = 5;
double arr[n]; // n is initialized with 5, so how is this different from Code 2?
P.S. This post answers using quotes from the standard, which I do not understand. I will very much appreciate an answer that uses a simpler language.
n is not a constant expression type!
There is no such thing as a constant expression type. n in that example is a expression, and it is in fact a constant expression. And that is why it can be used as the array size.
It is not necessary for a variable to be declared constexpr in order for its name to be a constant expression. What constexpr does for a variable, is the enforcement of compile time constness. Examples:
int a = 42;
Even though 42 is a consant expression, a is not; Its value may change at runtime.
const int b = 42;
b is a constant expression. Its value is known at compile time
const int c = rand();
rand() is not a constant expression, and so c is neither. Its value is determined at runtime, but may not change after initialisation.
constexpr int d = 42;
d is a constant expression, just like b.
constexpr int f = rand();
Does not compile, because constexpr variables must be initialised with a constant expression.
then why does the following not compile:
Because the rules of the language don't allow it. The value of n is not compile time constant. The value of a non-const variable can change at runtime.
The language cannot have a rule that some value doesn't change at runtime, then it is a constant expression. That would not be of any use to the programmer since they cannot assume which compiler will be able to prove the constness of which variable.
The language has to exactly specify the cases where an expression is constant. It would also be infeasible to specify that a non-const variable is a constant expression if it hasn't been modified before its use, because it is impossible to prove in most cases, even though you've found one case where the proof happens to be easy.
// n is not a constant expression type!
But it is. Per [expr.const]/3
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is a constant-initialized variable of reference type or of const-qualified integral or enumeration type. An object or reference is usable in constant expressions if it is [...]
a complete temporary object of non-volatile const-qualified integral or enumeration type that is initialized with a constant expression.
So, if you have a const integer intialized with a constant expression then you still have a constant expression as nothing can change. This is a rule that existed before constexpr was ever a thing as it allowed programmers to initialize arrays with constant variables instead of using macros.
Furthermore, if the compiler is smart enough to see that n is initialized with 5, then why does the following not compile:
Because the integer is not const so it could be changed. Even though in your case you can prove it can't change, in general you can't so it is just not allowed.
A value declared constexpr means that this value does not change and is known during compile time.
A value declared const means that this value does not change after initialization, but it is not mandatory to be known during compile time. In other words, a constexpr is const, but a const is not constexpr.
Your "Code 3" example doesn't work because you need a constant known at compile time in order to allocate memory for a vector, so you need a constexpr.
Why this type of declaration
int nArraySize = 7;
char szName[nArraySize] = "Mollie";
returns this error:
error: variable-sized object 'szName' may not be initialized
but when I declare the 'arraySize' variable as a 'const int' it works ?
const int nArraySize = 7;
char szName[nArraySize] = "Mollie";
It must be said first that in C++ language, the size part of array declaration is required to be an Integral Constant Expression (ICE). A const int object declared with an initializer can be used in an ICE. An int object cannot be used in an ICE. That's the formal part of it.
However, judging by the error message, your C++ compiler supports C99-style variable-length arrays (VLA) in C++, as a non-standard extension. That means that in your compiler you are allowed to use non-constant expressions to specify size in array declarations. Yet even if VLAs themselves are supported, such arrays still cannot be initialized. This is prohibited by the specification of VLAs in C99, and that is exactly how their specification is "inherited" by your C++ compiler.
In other words, contrary to what other answers stated, this code will probably be accepted by your C++ compiler
int nArraySize = 7;
char szName[nArraySize];
even though it is formally illegal C++. It is the = "Mollie" part that triggers the error.
Because C++ does not support variable-length arrays (introduced in the C-99 standard, but not in any version of C++). When you declare nArraySize as a non const int, the compiler complains because nArraySize may change at runtime. If nArraySize is const, the compiler knows that it cannot change at runtime, and therefore the array size of szName cannot be variable (i.e. can be deduced at compile time). In C++ (and versions of C before C99), the size of an array must be a constant that can be deduced at compile-time.
Because the program has to know at compile time how much memory to alocate for your variables. When you don't make your nArraySize constant it is assumed it may change during runtime. While making it constant asures the compiler this value will not be changed.
The first is a variable length array and it's standardized in C (since the C99 standard) but not in C++.
C++ needs all arrays to have their sizes available at compile time, not runtime. Declaring the size as a constant makes it a compile-time constant.
The Standard does not permit dynamically-sized, statically-allocated arrays. You may find that in GCC you will be able to do this, but that is because that is one of a number of extensions that allow non-conforming behavior.
An array is defined like this:
D1 [ constant-expressionopt] attribute-specifier-seqopt
Where the size is an integral constant expression. The Standard defines an integral constant expression like this:
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [Note: Such expressions
may be used as array bounds (8.3.4, 5.3.4), [...] — end note]
int n = 10;
int x[n]; // error!
The variable n is not a constant expression, so it will not work for this case.
By adding constexpr (C++11) to the type, it will be useable in a constant expression. But in this case const is enough to make it work:
int const n = 5;
int x[n];
On the other hand, dynamic arrays take a dynamic size specifier:
int n = 10;
int *x = new int[n];
But an option I would recommend using is std::vector which is a wrapper around a dynamically-sized buffer:
#include <vector>
int main()
{
int n = 10;
std::vector<int> x(n); // x.size() is 10
}
I hope this helped.