Как extern работает в пространствах имен?

Я запускаю простую программу, похожую на ту, что я нашел здесь. Это предназначено для уменьшения раздувания кода при включении констант в несколько файлов. Он делает это, используя константные глобальные переменные в пространстве имен с их соответствующими предварительными объявлениями extern.

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;
}

Я предполагаю, что Constants::pi получает значение pi, содержащееся в пространстве имен констант globals.cpp, и может это сделать, поскольку имеет доступ к самому пространству имен из включенного globals.h. Чего я не понимаю, так это зачем глобальным определениям/инициализациям const в globals.cpp нужно ключевое слово extern? Я попытался удалить ключевые слова extern в globals.cpp, думая, что это не нужно, но моя программа не будет работать без них. Я думал, что extern используется только для предварительных объявлений? Зачем они нужны для глобальных определений/инициализаций const? Это как-то связано с пространством имен, в котором они определены?


person Wandering Fool    schedule 28.05.2015    source источник


Ответы (1)


Это предназначено для уменьшения раздувания кода при включении констант в несколько файлов.

Я бы предложил не сосредотачиваться на такого рода оптимизациях, если это не станет действительно необходимым, а выбрать самый простой дизайн: определить эти константы непосредственно в заголовочном файле и включить этот заголовочный файл из всех необходимых единиц перевода (.cpp-файлов). для доступа к этим константам. Поскольку эти объекты const, они будут иметь внутреннюю связь, и компоновщик не будет кричать на вас из-за нарушений правила одного определения.

Я попытался удалить ключевые слова extern в globals.cpp, думая, что это не нужно, но моя программа не будет работать без них.

Это связано с тем, что объекты области имен const со статической продолжительностью хранения (например, ваша переменная pi) имеют внутреннюю связь, если только вы явно не определите их как extern.

Я думал, что extern используется только для предварительных объявлений?

extern используется для объявления переменной, которая определена в другой единице перевода (файле .cpp). Если объект равен const, единица перевода, которая его определяет, должна явно пометить его как extern, чтобы он имел внешнюю связь и был виден из других единиц перевода (в этом не было бы необходимости, если бы объект не был const).

Это как-то связано с пространством имен, в котором они определены?

Нет, это правило для всех объектов const уровня пространства имен со статической продолжительностью хранения, и оно указано в параграфе [basic.link]/3 стандарта C++:

Имя, имеющее область пространства имен (3.3.6), имеет внутреннюю связь, если оно является именем

(3.1) [...] — переменная, функция или шаблон функции, явно объявленный статическим; или,

(3.2) — переменная энергонезависимого типа с квалификатором const, которая не объявлена ​​явно как extern и не объявлена ​​ранее как имеющая внешнюю связь; или

(3.3) — элемент данных анонимного объединения.

person Andy Prowl    schedule 28.05.2015
comment
Я чувствую себя частично вынужденным проголосовать против, потому что вы не указали, насколько глупым является его сценарий, а вместо этого сосредоточились только на языковых юристах, которые будут иметь ограниченную ценность для спрашивающего. - person Puppy; 29.05.2015
comment
@Puppy: не стесняйтесь минусовать. Ваша точка зрения верна. Имейте в виду, однако, что это не изменяемые переменные, как вы написали в комментарии к вопросу. Они являются const объектом. Но достаточно справедливо, я отредактирую. - person Andy Prowl; 29.05.2015
comment
Это не так, но другие примеры на странице, с которой он взял код, есть. - person Puppy; 29.05.2015
comment
Дамы, я, вероятно, буду использовать глобальные переменные только один или два раза в своей жизни. Я здесь только для того, чтобы понять язык, поэтому, если я столкнусь с плохо написанным кодом, я буду знать, что это значит. @AndyProwl Спасибо за подробное объяснение. Теперь я понимаю большую часть этого, за исключением двух вещей. На веб-сайте, с которого я это снял, говорится, что переменные внутри namespace Constants являются настоящими глобальными переменными, но я думал, что пространство имен имеет область действия, и поэтому что-либо внутри не может иметь глобальную область видимости? И Constants::pi получает идентификатор из предварительного объявления pi в globals.h? - person Wandering Fool; 29.05.2015
comment
@WanderingFool: они находятся на уровне пространства имен, и их обычно называют глобальными, потому что они не входят в область действия блока, но это также не совсем точно, поскольку они не являются частью глобального пространства имен. Они имеют статическую продолжительность хранения, создаются до ввода main() и уничтожаются после его выхода, но они живут в пространстве имен Constants, и для доступа к их имени вы должны указать Constants:: (или сделать это неявно с помощью директивы using или объявление использования). Я предполагаю, что ответ на последний вопрос да: имя видно благодаря заголовку - person Andy Prowl; 29.05.2015