array bound is not an integer constant

V

Vladimir Jovic

Hello,

Consider next example (consisting of 3 source and 2 header files) :

/////// fda0.hpp
#ifndef FDA0
#define FDA0
extern const unsigned int TOTAL_SIZE;
#endif

/////// fda0.cpp
#include "fda0.hpp"
const unsigned int TOTAL_SIZE( 10 );

/////// fda1.hpp
#ifndef FDA1
#define FDA1
#include "fda0.hpp"
struct A
{
void print();
unsigned int buffer[ TOTAL_SIZE ];
};
#endif

/////// fda1.cpp
#include "fda1.hpp"
#include <iostream>
void A::print()
{
for ( unsigned int i=0; i < TOTAL_SIZE; ++ i )
{
std::cout<<buffer<<" ";
}
std::cout<<std::endl;
}

/////// fda2.cpp
#include "fda1.hpp"
int main()
{
A a;
a.print();
}


I am using g++ 4.3.0 to compile it as:
g++ fda0.cpp fda1.cpp fda2.cpp
and the error I am getting is next:
g++ fda1.cpp fda0.cpp fda2.cpp
In file included from fda1.cpp:3:
fda1.hpp:11: error: array bound is not an integer constant
fda1.cpp: In member function ‘void A::print()’:
fda1.cpp:11: error: ‘buffer’ was not declared in this scope
In file included from fda2.cpp:3:
fda1.hpp:11: error: array bound is not an integer constant


What is the cause of this error and how to fix it?

Thanks in advance
 
V

Vladimir Jovic

Vladimir said:
/////// fda0.hpp
#ifndef FDA0
#define FDA0
extern const unsigned int TOTAL_SIZE;
#endif

/////// fda0.cpp
#include "fda0.hpp"
const unsigned int TOTAL_SIZE( 10 );


Changing this to:

/////// fda0.hpp
#ifndef FDA0
#define FDA0
const unsigned int TOTAL_SIZE = 10;
#endif

and removing fda0.cpp, compiles the example fine. But the question still
stands : why?
 
I

Ian Collins

Vladimir said:
Hello,

Consider next example (consisting of 3 source and 2 header files) :

/////// fda0.hpp
#ifndef FDA0
#define FDA0
extern const unsigned int TOTAL_SIZE;
#endif

/////// fda0.cpp
#include "fda0.hpp"
const unsigned int TOTAL_SIZE( 10 );

/////// fda1.hpp
#ifndef FDA1
#define FDA1
#include "fda0.hpp"
struct A
{
void print();
unsigned int buffer[ TOTAL_SIZE ];
};
#endif

/////// fda1.cpp
#include "fda1.hpp"
#include <iostream>
void A::print()
{
for ( unsigned int i=0; i < TOTAL_SIZE; ++ i )
{
std::cout<<buffer<<" ";
}
std::cout<<std::endl;
}

/////// fda2.cpp
#include "fda1.hpp"
int main()
{
A a;
a.print();
}


I am using g++ 4.3.0 to compile it as:
g++ fda0.cpp fda1.cpp fda2.cpp
and the error I am getting is next:
g++ fda1.cpp fda0.cpp fda2.cpp
In file included from fda1.cpp:3:
fda1.hpp:11: error: array bound is not an integer constant
fda1.cpp: In member function ‘void A::print()’:
fda1.cpp:11: error: ‘buffer’ was not declared in this scope
In file included from fda2.cpp:3:
fda1.hpp:11: error: array bound is not an integer constant


What is the cause of this error and how to fix it?


The compiler is telling you - in the context of fda1.cpp, TOTAL_SIZE is
unknown. It has been declared, bit not defined. The compiler can't see
the definition in fda0.cpp while compiling fda1.cpp.
 
V

Victor Bazarov

Vladimir said:
Changing this to:

/////// fda0.hpp
#ifndef FDA0
#define FDA0
const unsigned int TOTAL_SIZE = 10;
#endif

and removing fda0.cpp, compiles the example fine. But the question still
stands : why?

Why what? By removing 'extern' from the declaration and adding the
initialiser (the "=10" part) you've made it into a definition. Suddenly
your 'TOTAL_SIZE' is defined in any translation unit that includes it.
That, in combination with the fact that 'TOTAL_SIZE' is a const, allows
the compiler to use it where a compile-time constant expression is
expected. The compiler substitutes 'TOTAL_SIZE' in brackets with the
values it has (10) and declares your array of fixed size.

V
 
V

Vladimir Jovic

Victor said:
Why what? By removing 'extern' from the declaration and adding the
initialiser (the "=10" part) you've made it into a definition. Suddenly
your 'TOTAL_SIZE' is defined in any translation unit that includes it.
That, in combination with the fact that 'TOTAL_SIZE' is a const, allows
the compiler to use it where a compile-time constant expression is
expected. The compiler substitutes 'TOTAL_SIZE' in brackets with the
values it has (10) and declares your array of fixed size.

What should I do as a rule of thumb in such cases where I have constants
in a header that is included in lots of files? Not necessarily integer
constants like in this example. Should I declare them extern in the
header whenever it is possible?
 
J

Juha Nieminen

Vladimir said:
extern const unsigned int TOTAL_SIZE;

Why would you even want a const integral to be extern? What would be
the advantage? As you yourself noticed, there are only disadvantages.

It's ok to put const integrals in headers even if those headers are
included in more than one compilation unit. The linker won't bark at
you. There's no need for 'extern'.
 
J

James Kanze

Vladimir Jovic wrote:
The compiler is telling you - in the context of fda1.cpp,
TOTAL_SIZE is unknown. It has been declared, bit not defined.
The compiler can't see the definition in fda0.cpp while
compiling fda1.cpp.

Note that technically, at least as I read it, the standard
requires his code to work: "An integral constant-expression can
involve only [...] const variables or static
data members of integral or enumeration types initialized with
constant expressions. [...]". His array dimension consisted of
just a const variable of integral type, initialized with a
constant expression. The fact that the initialization was in a
file not part of the translation unit doesn't seem to be a
constraint according to the standard.

This is, of course, an oversight, or a case of poor wording in
the standard; I'm sure that there was never an intent that
initializations in other translation units be taken into
account, and implementing it would be extremely difficult, if
not impossible. All of the compilers I know do require the
initialization to be present in the same translation unit. On
the other hand, I'm not sure what the intent is for something
like:

extern int const k ;
double a[ k ] ;
int const k = 10 ;

All of the compilers I have reject it, but no matter how you
look at it, k is a const variable of integral type initialized
with a constant expression. Which should make it legal.

I think I'll raise the issue in comp.std.c++.

(In the meantime, of course, just assume that the initialization
must be visible at the point of use. That seems to correspond
to what most compilers do, regardless of what the standard may
or may not say.)
 
J

James Kanze

Why would you even want a const integral to be extern?

Why wouldn't you?
What would be the advantage? As you yourself noticed, there
are only disadvantages.

Are you sure. Consider:

#include <vector>

const int i = 42 ;

inline // or make it a template...
void f()
{
std::vector< int > v ;
v.push_back( i ) ;
}

This is undefined behavior. It wouldn't be if i were extern.
(Of course, in practice, it will work with all compilers today.
Because no compiler currently checks for violations of the one
definition rule, and of course, since the address/reference
It's ok to put const integrals in headers even if those
headers are included in more than one compilation unit. The
linker won't bark at you. There's no need for 'extern'.

The semantics are different. By default, a variable defined as
const has internal linkage; the extern forces it to have
external linkage. There are several cases where this makes a
difference. The above is an example: the symbol "i" in the
inline function "f" binds to different entities in different
translation units. (There is a special exception which applies
to const objects, but it only applies if only the value of the
object, and not its address, is used. Binding the object to the
int const& parameter of vector<>::push_back uses the address.)
There are also cases which may affect real code:

template< int const& ri >
class T { /* ... */ } ;

int const i = 42 ;

T< i > aT ;

won't compile, for example, unless you use "extern" to force i
to have external linkage. (I've actually been bitten by this in
real code, and had to add the extern to my constant objects.)

One obvious solution to this (today---the problem is historical)
would be to require the compiler to handle const objects with
namespace scope exactly like it now handles static template
data members. (Alf's suggestion.)
 
V

Vladimir Jovic

Juha said:
Why would you even want a const integral to be extern? What would be
the advantage? As you yourself noticed, there are only disadvantages.

It's ok to put const integrals in headers even if those headers are
included in more than one compilation unit. The linker won't bark at
you. There's no need for 'extern'.

Last week I had all constants defined in few headers, and they were of
various types (integer, strings, some objects, etc). Then I changed
strings and objects to be extern. Then I wanted to change integer
constants to be extern as well, but I got that error.

I guess the reason to do that is consistency, and maybe laziness (now I
have to open two files if I want to change a constant).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top