C++: Свързване на външни променливи от пространство от имена

Изглежда не мога да препратя външно дефинирана променлива от пространство от имена, използвайки extern. Работи от глобалния обхват, но веднага щом там бъде хвърлено пространство от имена, то не успява да се свърже.

Моят файл с константи изглежда така:

StringConstants.cpp

#include "MyString.h"

MyString test1("string1");

MyString test2("string2");

Основната програма изглежда така:

main.cpp

#include <stdio.h>
#include "MyString.h"

extern MyString test1;

namespace {
    extern MyString test2;
}

int main(void) {
    printf("%s\n", test1.Str());
    printf("%s\n", test2.Str());
}

Получавам подобни грешки както в GCC, така и в Visual Studio:

gcc    main.o StringConstants.o   -o main
main.o:main.cpp:(.text+0x49): undefined reference to `(anonymous namespace)::test2'
collect2: ld returned 1 exit status

1>Linking...
1>main.obj : error LNK2001: unresolved external symbol "class MyString `anonymous namespace'::test2" (?test2@?A0x0df4aa01@@3VMyString@@A)
1>C:\p4\namespace_repro\namespace_repro2\Debug\namespace_repro2.exe : fatal error LNK1120: 1 unresolved externals

Опитах се да квалифицирам препратката към test2 (extern MyString ::test2), но тя просто смята, че test2 е статичен член на MyString. Именуваното пространство от имена не се държи по различен начин от анонимното. Поради различни причини не искаме да премахваме пространствата от имена или да поставяме екстерните извън пространствата от имена.

Ето и другите файлове за пълнота:

MyString.h

class MyString {
public:
   MyString(const char* str): mStr(str) {};
   const char* Str() const { return mStr; }
private:
   const char* mStr; 
};

Makefile

CC=gcc 
CFLAGS=-Wall

main: StringConstants.o main.o

Целите на тази система са всички константи да бъдат дефинирани в един файл и да бъдат разрешени по време на свързване, вместо да бъдат в заглавка. Изглеждаше, че горният код ще работи, но тъй като е отхвърлен от два различни линкера, изглежда, че разбирането ми за C++ не е достатъчно добро. Съвет как да накарате това да работи, освен да поставите екстерните извън пространствата от имена?


person breath    schedule 18.11.2010    source източник


Отговори (2)


$7.3.1/2 - "Всяко име, декларирано за първи път в пространство от имена, е член на това пространство от имена."

Това означава, че името „test2“ е част от анонимното пространство от имена и трябва да бъде дефинирано в това пространство от имена, ако се използва.

Проблемът не е само в анонимното пространство от имена, а във всяко пространство от имена.

person Chubsdad    schedule 18.11.2010
comment
въздишка Предполагам, че не мога да не се съглася със спецификацията. Разгледах този раздел няколко пъти, но не разбрах значението на този конкретен ред. Благодаря, че го изписахте. - person breath; 18.11.2010

Какво...

namespace { 
    extern MyString test2; 
} 

...това е да се каже, че test2 трябва да съществува в анонимно пространство от имена, но не е - той е в глобалното пространство от имена. Вие сте излъгали вашия компилатор, следователно той генерира обект, който няма да се свърже. Трябва декларацията extern да бъде направена от същия обхват на пространството от имена като променливата.

НО трябва да има StringConstants.h, който main.cpp трябва да включва, така че единицата за компилиране да знае за низовете без допълнителни инструкции.

person Tony Delroy    schedule 18.11.2010
comment
+1, анонимното пространство от имена е виновникът тук. Имайте предвид, че той все още ще трябва да използва extern, независимо дали е в заглавка или не. - person avakar; 18.11.2010
comment
Проблемът с наличието на StringConstants.h е, че основно ще се наложи повторно компилиране на всеки файл в програмата, когато някой модифицира или добави константа (което ще бъде доста често). Има ли някакъв начин да кажа на компилатора, че искам да се позова на глобален test2? - person breath; 18.11.2010
comment
@breath: ако нещата са толкова непостоянни, липсата на споделен хедър ще бъде много по-лошо: какво се случва, когато някой разбере, че някаква стойност трябва да бъде променена от int на double? Програмите продължават да се изграждат и свързват и се провалят по време на изпълнение. Ако споделените заглавки наистина са твърде трудни, тогава помислете дали да ги разделите с отделни заглавки за всички непостоянни части, от които се нуждаят по-малко единици за превод. Помислете за pImpl, интерфейси и други абстракции, които помагат за отделяне на клиентите от внедряването. - person Tony Delroy; 18.11.2010
comment
Е, всички те са низови константи, така че никой няма да променя никакви типове. Добре сме с липсващи константи, което води до грешки в линкера. По-осъществимо решение за нас е просто да преместим extern декларациите извън пространствата от имена в изходните файлове или просто да изтръгнем всички наши пространства от имена и просто да преминем към командос, така да се каже, но нито една от двете не изглежда като задоволителна опция. Ако не може да се направи по друг начин, не може да се направи, но наистина изглежда като нещо, което би трябвало да е възможно. :) - person breath; 18.11.2010
comment
@breath: Помислете за <iosfwd> - той предоставя предварителни декларации за класове в <iostream>, но въпреки това се поддържа заедно с него (и в идеалния случай - особено в непостоянна софтуерна система - ще бъде включен от него, за да се гарантира последователност). Не бихте ли могли да направите същото: да имате допълнителни заглавки за външна декларация за всяко клиентско приложение / единица за превод логически под заглавката на всички низове, като последната ги включва, за да провери последователността...? - person Tony Delroy; 18.11.2010
comment
Бихме могли, но всъщност никога няма да променяме стойностите на низовите константи, а само тяхното членство, така че през повечето време пак ще прекомпилираме цели модули. :( Мисля, че простото поставяне на externs извън пространствата от имена, макар и непривлекателно, поне ограничава повторното компилиране само до модифицирания .cpp. - person breath; 18.11.2010
comment
Оценявам помощта ти, Тони, съжалявам, че нашият случай на използване е толкова различен от нормата. - person breath; 18.11.2010
comment
@breath: без притеснения... правилният отговор винаги е този, който работи най-добре при дадените обстоятелства. наздраве - person Tony Delroy; 19.11.2010