1

I get the following error when I use constant nVar instead of a number.

constants.h:

extern const unsigned int nVar;

constants.cpp:

#include "constants.h"
const unsigned int nVar = 5;

main.cpp

#pragma once
#include "constants.h"
void foo(const double q[nVar])
{
    // ...
}

Compiler:

array constant is not an integer constant before ']' token

expected ')' before ',' token

expected unqualified-id before 'const'

Shibli
  • 5,879
  • 13
  • 62
  • 126

2 Answers2

5

I think it happens because compiler should know array's size at compile time, but in your example value of nVar will be known only at linking time due to extern

Evgeny Eltishev
  • 593
  • 3
  • 18
  • 1
    Correct. Move the assignment (=5) part to the header file and it will compile fine - in that case the compiler knows the size of that array. – parry May 31 '13 at 21:36
  • @parry: Firstly, `= 5` is not assignment, it is initialization. Secondly, simply moving `= 5` to the header file will immediately trigger ODR violations (typically reported as "multiple definition" linker errors). In order to be able to specify `= 5` in the header file the object has to be given internal linkage, i.e. `extern` has to be removed. – AnT stands with Russia May 31 '13 at 22:43
  • AndreyT Pedantry was never my interest :) It is besides the point too which was that the compiler should know the size of the array when it encounters it. I am sure we can trust OP to put the initialization and assignments in the right place - I wasn't suggesting it as a best practice, just illustrating that it would make the size visible to the compiler. – parry Jun 01 '13 at 01:42
1

Firstly, you are not "initializing" the array with a constant in our example. You are specifying the size of the array. Note that in the given example the array size will be ignored anyway. Your declaration

void foo(const double q[nVar])

is actually equivalent to

void foo(const double q[])

and to

void foo(const double *q)

Secondly, in order for integral constant to be usable in a constant expression it has to be declared with an initializer. In your main.cpp your constant is declared without an initializer, which means that it can't form constant expressions and can't be used in array declarators.

Unless you really need a const object with external linkage, the proper way to declare your constant would be

const unsigned int nVar = 5;

right in the header file. Note: no extern and the initializer is specified right in the header file. The definition in constants.cpp has to be removed in that case. Technically, this will create an independent nVar object with internal linkage in each translation unit, but it won't normally occupy any memory unless used as an lvalue.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • That doesn't sound right to me, this sounds like the rules for a `static const` member variable, but that doesn't apply to `const` global variables does it? And even `static const` need defining in a translation unit if you want to use it as an lvalue. – Mooing Duck May 31 '13 at 22:36
  • @Mooing Duck: I'm not sure what you are trying to say and what it has to do with member variables. Namespace-scope variable declaration with an initializer is always a *definition*, meaning that it is already defined. No need to define anything in a translation unit. As for whether it will occupy memory if used only in constant expressions - it is largely a quality-of-implementation issue. – AnT stands with Russia May 31 '13 at 22:40
  • I always thought this would result in ODR violations unless it was marked as `extern`. – Mooing Duck May 31 '13 at 22:55
  • @Mooing Duck: In C++ (as opposed to C) namespace-scope `const` objects have internal linkage by default, i.e. plain `const` is equivalent to `static const`. For that reason it does not produce ODR violations. – AnT stands with Russia May 31 '13 at 23:58
  • oh, never knew that. Makes sense I guess. – Mooing Duck Jun 01 '13 at 00:23