views:

86

answers:

3

I tried

const int i[] = { 1, 2, 3, 4 };
float f[i[3]]; // g++ cries "error: array bound is not an integer constant"

int main()
{
   const int j[] = { 0, 1, 2, 3 };
   float g[j[3]];                   // compiler is happy :)

   return 0;
}

What is the difference between the two aggregates? How come referring to a const aggregate's element inside main() is valid when it's invalid @ the global scope?

+10  A: 

In C++ sizes in array declaration have to be Integral Constant Expressions (ICE). By definition, ICE in C++ cannot include a value taken from an array, regardless of whether it is a const array or not. So, in both cases i[3] and j[3] are not ICEs and cannot be used as sizes in array declarations. For this reason both declarations - f and g - are illegal in C++.

However, since you are using GCC, in the second case a non-standard compiler extension comes into play. When you are declaring an automatic array, GCC allows you to specify a non-constant size (i.e. run-time size) for the array (this is basically C99's VLAs carried over to C++). This is why it is "happy", even though the code is not legal in C++. Compile in -ansi -pedantic-error mode and you should get a diagnostic message (an error) for each declaration.

AndreyT
I get it, I tried it with the said options and made the compiler cry again :) But when I tried, in global scope, `const int i = 100; const int j = i + 10; int k = [j+10];` even with those options ON, it compiles fine. Why? Shouldn't it say that even `j` isn't a ICE?
legends2k
legends2k: No, `j` is not an ICE, since it is based on the value of `i`, and `i` is not an ICE. What exactly did you try this time (the code in the comments looks incomplete)? What "compiles fine"?
AndreyT
As for the original example, my GCC 3.4.4 reports errors for both array declarations in `-ansi -pedantic` mode.
AndreyT
@AndreyT: I was just trying that decl. in my global scope with just an empty main() like `const int i = 100; const int j = i + 10; int k[j+10]; int main(int argc, char *argv[]) { return 0; }` using my GCC 4.4.1 with `-Wall -ansi -pedantic-errors` it compiles fine. No errors or warnings.
legends2k
@legends2k: in your new code in these comments, `i` and `j` are both ICEs, as is `j+10`.
Steve Jessop
@legends2k: As it should. There are no problems in this version. Array size is ICE in this case, so it compiles.
AndreyT
I just came to know; `const int i = 10; int j[i];` is valid, while `const int i[] = {1, 10, 100}; int j[i[2]];` isn't; because the former is a compile time constant (kept in symbol table) and the later is a aggregate and hence storage will be created for it and the will not know of it's value, like Neeraj has pointed out http://stackoverflow.com/questions/2260386/referring-to-const-value-compile-time-when-is-a-consts-definition-really-ava/2260449#2260449.
legends2k
@legends2k: "Practical" explanations, like "storage will be created... etc." don't really explain anything in cases like this. In reality, there's no problem for the compiler to retrieve a value from a constant array by a constant index at compile time. It is just that the language standard decided to disallow this in ICEs. The standard doesn't allow this - this is the only explanation.
AndreyT
A: 

GCC 3.4.5 says "variable-size type declared outside of any function" and 4.4.0 says "array bound is not an integer constant" for f[i[3]], which leads me to believe that neither i[3] nor j[3] is constant, though I couldn't tell you why. If I had to take a wild guess I'd say perhaps it has something to do with the addresses of i and j not being known until link time, but I'm really not sure.

Edit: I got beaten to it. Leaving behind my inferior answer for posterity.

Jon Purdy
+1  A: 

Compiler can use those constants as compile-time constants, which it can keep in its symbol table. But virtually,no compiler is sophisticated enough to store an aggregate in its symbol table, so the value can't be used at compile time.
Furthermore non-standard gcc extensions allow you to use variable length automatic arrays. Thats's why not only this:

const int j[] = { 0, 1, 2, 3 };
   float g[j[3]]; 

but also this is allowed by the compiler:

int j[] = { 0, 1, 2, 3 };
   float g[j[3]];
Neeraj
Or for that matter the VLA extension allows `int j = std::rand(); float g[j];`, although I don't recommend it...
Steve Jessop
@Neeraj: Thats the reason `const i = 100; int j[i];` works fine; while `const i[] = {1, 10, 100}; int j[i[2]];` doesn't. Thanks :)
legends2k