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

Изпълнявам проста програма, подобна на това, което намерих тук. Има за цел да намали раздуването на кода, когато включва константи в множество файлове. Той прави това, като използва глобални променливи const в пространство от имена със съответните им 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 Constants, и е в състояние да го направи, защото има достъп до самото пространство от имена от включени globals.h. Това, което не разбирам, е защо const глобалните дефиниции/инициализации в globals.cpp имат нужда от ключовата дума extern? Опитах се да премахна extern ключовите думи в globals.cpp, мислейки, че не е необходимо, но програмата ми няма да работи без тях. Мислех, че extern се използва само за предварителни декларации? Защо са необходими за const глобалните дефиниции/инициализации? Има ли нещо общо с пространството от имена, в което са дефинирани?


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


Отговори (1)


Има за цел да намали раздуването на кода, когато включва константи в множество файлове

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

Опитах се да премахна външните ключови думи в 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:: (или да направите това имплицитно чрез директива за използване или използваща декларация). Предполагам, че отговорът на последния въпрос е да: името се вижда благодарение на заглавката - person Andy Prowl; 29.05.2015