5

I have some const variables that I would like the values of to be shared between multiple source files. I would also like the variable's scope to be limited to a namespace. I am unsure of the best/correct way to do this?

I could use a #define but want to have the type safety.

So far I have the following which works:

File0.h

#pragma once

namespace Namespace1
{
   extern const int variable1;
   extern const int variable2;
}

File0.cpp

const int Namespace1::variable1 = 10;
const int Namespace1::variable2 = 10;

Source1.cpp

#include "File0.h"
int result1 = Namespace1::variable1 + Namespace1::variable2;

Source2.cpp

#include "File0.h"
const int result2 = Namespace1::variable1 + Namespace1::variable2;

With extern how do I know when the value has been initialized?

1 Answers1

7

With extern how do I know when the value has been initialized?

You don't. That is known as the static initialization order fiasco. Initialization of namespace scope static objects in different translation units is done in an unspecified order. If one static object depends on another object in a different translation for its initialization, the behavior is undefined.

Even with simple integers, this catastrophe can occur. Since your intention is to avoid macros (a worthy goal) you can just define those constants in the header:

namespace Namespace1
{
    const int variable1 = 10;
    const int variable2 = 10;
}

That will not be a one-definition-rule violation, since the C++ standard (even in 2003) allows for such integral constants to be defined in multiple translation units by making them implicitly have internal linkage. They are constant expressions as well, just like a macro will produce.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Namespace scope const variables are implicitly static, and therefore not exactly equivalent to original snippet. – Passer By Sep 12 '18 at 07:32
  • 1
    @PasserBy - I should hope it's not equivalent. The original snippet is subject to being broken by initialization order. – StoryTeller - Unslander Monica Sep 12 '18 at 07:35
  • I meant they have different semantics, along with sidestepping the fiasco. – Passer By Sep 12 '18 at 07:38
  • Since "Namespace scope const variables are implicitly static" does that mean the following are equivalent? `namespace Namespace1 { const int variable1 = 10; }` `namespace Namespace1 { static const int variable1 = 10; }` – Alex Angell Sep 12 '18 at 08:41
  • @AlexAngell - As far as linkage is concerned yes. But if you are worried about bloating your executable, don't be. It existed in C++ since prehistoric times, so unless you take the address of `variable1`, it won't occupy any storage. For instance, on GCC 4.1 https://godbolt.org/z/oOcjEk , even with optimizations disabled. A true constant. – StoryTeller - Unslander Monica Sep 12 '18 at 08:49
  • The const variables have implicit internal linkage, so every translation unit gets its own variable copy (with the same value of course). with c++ 17 you could declare the variable inline to truly share the exact same variable. maybe you can add this to your answer (ups just saw the c++03). never mind then :x – phön Sep 12 '18 at 11:43
  • @phön - *It's own variable copy* is quite loaded. Unless the variable's address is taken there is very likely no copy (in the sense that a copy is an object occupying storage). – StoryTeller - Unslander Monica Sep 12 '18 at 11:46
  • @StoryTeller yeah. But if you take the addresses and compare them (in 2 different TUs) they will be different, which could be surprising. Just wanted to be correct tho :-P – phön Sep 12 '18 at 12:18
  • 1
    @phön - Yeah, that's a sticky point, and something like [`max(variable1, variable2)` comes to mind](https://godbolt.org/z/sUsYFh)... but I still don't think such code is likely to come up a lot in practice. Not enough to warrant expanding the answer any more, anyway. – StoryTeller - Unslander Monica Sep 12 '18 at 12:26