28

Multiple definition of a global variable is not allowed in C or C++ due to the One Definition Rule. However, in C++ a const global variable can be defined in multiple compilation units with no error. This is not the same as in C.

Why does C++ allow this while C does not? Why does the usage and behaviour of a const global differ from a non-const global in this way in C++ compared to C? What is happening under the covers with C++ and C with respect to const?

For example this is allowed in C++, but wrong in C:

// Foo.cpp
const int Foo = 99;

// Main.cpp
const int Foo = 99;
int main()
{
    cout << Foo << endl;
    return 0;
}

And this is fine with C, but wrong with C++:

// Foo.cpp
const int Foo = 99;

// Main.cpp
extern const int Foo;
int main()
{
    cout << Foo << endl;
    return 0;
}
MSalters
  • 173,980
  • 10
  • 155
  • 350
Ashwin Nanjappa
  • 76,204
  • 83
  • 211
  • 292
  • 1
    You seem to have answered your own question...? Which part are you confused about? – Merlyn Morgan-Graham May 30 '11 at 08:21
  • 1
    I guess he is confused about the fact that he cannot declare a const int as extern without getting linking errors. – iolo May 30 '11 at 08:23
  • 1
    Merlyn: I know how to use const in C++. I am interested to know why const was designed like this and not like a normal global variable? – Ashwin Nanjappa May 30 '11 at 08:24
  • 1
    Arguably, this is a duplicate of http://stackoverflow.com/questions/998425/why-does-const-imply-internal-linkage-in-c-when-it-doesnt-in-c – MSalters May 30 '11 at 08:58

6 Answers6

28
// Foo.cpp
const int Foo = 99;

// Main.cpp
const int Foo = 99;

const variable at namespace scope has internal linkage. So they're basically two different variables. There is no redefinition.

From @David's comment, 3.5/3 [basic.link]:

A name having namespace scope (3.3.5) has internal linkage if it is the name of
— an object, reference, function or function template that is explicitly declared static or,
— an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage; or
— a data member of an anonymous union.


In the second case, you should be doing this (correct way):

//Foo.h
extern const int Foo; //use extern here to make it have external linkage!

// Foo.cpp
#include "Foo.h"
const int Foo = 99; //actual definition goes here

// Main.cpp
#include "Foo.h"
int main()
{
   cout << Foo << endl;
}
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Thanks Nawaz. Is this explained somewhere in the C++ Programming Language book or some other reference? – Ashwin Nanjappa May 30 '11 at 08:22
  • 6
    In the standard... 3.5/3 [basic.link]: *A name having namespace scope (3.3.5) has internal linkage if it is the name of — an object, reference, function or function template that is explicitly declared static or,— **an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage;** or — a data member of an anonymous union.* – David Rodríguez - dribeas May 30 '11 at 08:26
  • 1
    @David: I quoted your comment in my post. I was looking for this text in the Standard after @Ashwin's request, but you made the job easy. Hope you don't mind it. – Nawaz May 30 '11 at 08:30
  • I think should include `Foo.h` in `Foo.cpp` too, otherwise `Foo` is not declared with external linkage. – Matthieu M. May 30 '11 at 08:46
  • @Matthieu: True. I just forgot to do that. Thanks :-) – Nawaz May 30 '11 at 08:49
7

I think you are asking for the rationale and not the specific language rule that allows this.

The rationale for this is that it makes const variables much easier to use. It gives a typed replacement for one common use of #define.

Instead of #define MAX_COUNT 211 you can use const int max_count = 211; in exactly the same way, e.g. a shared header file, without having to worry about where to put the one definition.

You can't legally change the value of a const object so there's no visible difference between having one object and multiple objects with the same value.

As you can put a definition of a const object in a header file it makes trivial for the compiler to use the value directly at the compilation stage without such optimizations having to be delayed to a link-time fixup.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • Could you also explain why this C++ behaviour of const differs from C? The code above that works in C++ has multiple-definition error in C! – Ashwin Nanjappa May 30 '11 at 08:40
  • @Ashwin: I'm afraid not. I believe that C had more backwards compatibility issues (supporting tentative definitions?) and have ended up with a more compromised `const` but I don't have any direct evidence for this. – CB Bailey May 30 '11 at 08:43
  • @AshwinNanjappa I suppose C just prefers to keep keyword usage consistent in this case - if you specify static, you get internal linkage; otherwise, you get external linkage :) – martinkunev Nov 16 '15 at 14:13
6

Basically, in C++, const, non-local variables are genuine constant expressions, or constexpr. This permits plenty of things, like TMP.

const int five = 5;
int main() {
    int x[five];
    std::array<int, five> arr;
}

In C, they are just a variable that cannot be modified. That is,

const int five = 5;
int main() {
    int x[five]; // Technically, this is a variable length array
}

Is quite equivalent to

int five = 5;
int main() {
    int x[five];
}

Effectively, C++ promotes some kinds of const variable to a new category, constexpr, whereas in C, this does not exist and they are just variables which happen to be unmodifiable.

Puppy
  • 144,682
  • 38
  • 256
  • 465
3

It looks like const doesn't actually generate an external symbol.

Community
  • 1
  • 1
Alex B
  • 82,554
  • 44
  • 203
  • 280
-1

Why do English people spell COLOUR, whereas American people spells it COLOR?

They are 2 different languages from the same base, but they don't have the same rules.

C & C++ are the same. If they weren't different, they would both be called the same thing.

Neil
  • 11,059
  • 3
  • 31
  • 56
-3

My workaround was declaring it as :

static classfoo foo;

it worked in my situation.

hkf
  • 4,440
  • 1
  • 30
  • 44
Jak
  • 1
  • 1