Search code examples
c++namespacesglobal-variablesconstantsextern

How does extern work in namespaces?


I'm running a simple program similar to what I found here. It's meant to reduce code bloat when including constants in multiple files. It does this by using const global variables within a namespace with their respective extern forward declarations.

globals.h

#ifndef GLOBALS_H_
#define GLOBALS_H_

namespace Constants
{
    // forward declarations only
    extern const double pi;
    extern const double avogadro;
    extern const double my_gravity;
}

#endif

globals.cpp

namespace Constants
{
    // actual global variables
    extern const double pi(3.14159);
    extern const double avogadro(6.0221413e23);
    extern const double my_gravity(9.2); // m/s^2 -- gravity is light on this planet
}

source.cpp

#include <iostream>
#include <limits>

#include "globals.h"

int main()
{
    double value_of_pi = Constants::pi;

    std::cout << value_of_pi;

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();

    return 0;
}

I assume that Constants::pi gets the value pi contained in the globals.cpp Constants namespace, and is able to do so because it has access to the namespace itself from the included globals.h. What I don't understand is why the const global definitions/initializations in globals.cpp need the extern keyword? I tried removing the extern keywords in globals.cpp thinking that it wasn't needed, but my program won't run without them. I thought extern was only used for forward declarations? Why are they needed for the const global definitions/initializations? Has it something to do with the namespace they are defined in?


Solution

  • It's meant to reduce code bloat when including constants in multiple files

    I would suggest not to focus on this kind of optimizations unless it becomes really necessary, and rather choose the simplest design: define those constants directly in the header file and include that header file from all translation units (".cpp files") that need to access those constants. Since those objects are const, they will have internal linkage and the linker won't be screaming at you due to violations of the One Definition Rule.

    I tried removing the extern keywords in globals.cpp thinking that it wasn't needed, but my program won't run without them

    That's because namespace-scope const objects with static storage duration (like your pi variable) have internal linkage unless you explicitly define them as extern.

    I thought extern was only used for forward declarations?

    extern is used to declare a variable that is defined in another translation unit (".cpp file"). If the object is const, the translation unit that defines it needs to explicitly mark it as extern in order for it to have external linkage and be visible from other translation units (this wouldn't be necessary if the object were not const).

    Has it something to do with the namespace they are defined in?

    No, this is the rule for all namespace-level const objects with static storage duration, and it is specified in paragraph [basic.link]/3 of the C++ Standard:

    A name having namespace scope (3.3.6) has internal linkage if it is the name of

    (3.1) [...] — a variable, function or function template that is explicitly declared static; or,

    (3.2) — a variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external linkage; or

    (3.3) — a data member of an anonymous union.