Согласно пункту 3.5 / 4 стандарта C ++:
Безымянное пространство имен или пространство имен, объявленное прямо или косвенно
внутри безымянного пространства имен есть внутренняя связь.
Одновременно в пункте 7.3.1.1 мы имеем примечание 96):
Хотя сущности в безымянном пространстве имен могут иметь внешнюю связь,
они квалифицированы по имени, уникальному для их перевода
единица и, следовательно, никогда не будет видно из любой другой единицы перевода.
Как явно сделать внешнюю связь для имени внутри безымянного пространства имен и как проверить, что эта связь на самом деле является внешней, если Стандарт гарантировал, что нет способа получить доступ к имени, определенному внутри безымянного пространства имен, из другого модуля перевода?
В каких случаях полезно делать явную внешнюю связь для имени в безымянном пространстве имен?
Как явно сделать внешнюю связь для имени в безымянном пространстве имен
Единственный способ, которым я могу придумать, это дать ему связь на языке C, чтобы его имя связи игнорировало квалификацию пространства имен:
namespace {
extern void f() { } // has internal linkage despite 'extern'
extern "C" void g() { } // ignores linkage of namespace
}
void (*p)() = f; // ensure 'f' won't be optimized away
(Строгое чтение стандарта предполагает, что g
должна иметь внутреннюю связь, но компиляторы, похоже, этого не делают.)
и как проверить, что эта связь на самом деле внешняя, если Стандарт гарантирует, что нет способа получить доступ к имени, определенному внутри безымянного пространства имен, из другого модуля перевода?
Как правило, ELF-компиляторы реализуют внутреннюю связь с неглобальными символами, поэтому вы можете скомпилировать код и проверить объектный файл:
$ g++ -c linkage.cc
$ nm linkage.o
0000000000000000 t _ZN12_GLOBAL__N_11fEv
0000000000000007 T g
0000000000000000 D p
Искаженное имя безымянного пространства имен может варьироваться в зависимости от компилятора, но его деформирование покажет:
$ nm -C linkage.o
0000000000000008 t (anonymous namespace)::f()
0000000000000000 T g
0000000000000000 D p
Строчные буквы t
показывает, что f
имеет локальную видимость, что означает, что он не может быть связан с другими объектными файлами. Прописные буквы T
показывает, что g
имеет внешнюю связь.
Это не гарантируется стандартом, так как видимость ELF не является частью стандарта C ++, а некоторые компиляторы реализуют связывание без использования видимости даже на платформах ELF, например, EDG-компилятор создает глобальный символ для того же кода:
$ nm linkage.o
0000000000000008 T _ZN23_GLOBAL__N__7_link_cc_p1fEv
0000000000000004 C __EDGCPFE__4_9
0000000000000000 T g
0000000000000000 D p
$ nm -C linkage.o
0000000000000008 T (anonymous namespace)::f()
0000000000000004 C __EDGCPFE__4_9
0000000000000000 T g
0000000000000000 D p
Итак, используя extern "C"
позволяет дать имя внешней связи, даже если она появляется в безымянном пространстве имен, но это не делает примечание правильным, потому что вы Можно ссылайтесь на это имя из других единиц перевода, потому что оно не использует безымянную область пространства имен. Это наводит меня на мысль, что заметка является просто пережитком C ++ 03, когда сущности в безымянных пространствах имен не имеют автоматически внутренней связи, и заметка должна быть исправлена или удалена (и, действительно, Т.С. указывает, что она уже была удалена DR 1603).
В каких случаях полезно делать явную внешнюю связь для имени в безымянном пространстве имен?
Я не могу вспомнить ни одного случая, когда это полезно.
ре
» В каких случаях полезно делать явную внешнюю связь для имени в безымянном пространстве имен?
Необходимость внешней связи была важна для шаблонов C ++ 03. Например. указатель на функцию в качестве аргумента шаблона должен быть указателем на функцию внешней связи. Например, следующее не будет компилироваться с компилятором C ++ 03:
template< void(*f)() >
void tfunc() { f(); }
#include <stdio.h>
static void g() { printf( "Hello!\n" ); }
int main()
{
tfunc<g>();
}
Он прекрасно компилируется с помощью компилятора C ++ 11.
Таким образом, в C ++ 11 механизм анонимного пространства имен для внешней связи, но без конфликтов имен между единицами перевода, технически необходим только для классов. У класса есть внешняя связь. Не хотелось бы выбирать имена классов, которые гарантированно не встречались в других единицах перевода.
С С ++ 11 правила изменились не только для параметров шаблона, но и для того, имеют ли вещи в анонимном пространстве имен внешнюю связь или нет. В C ++ 03 анонимное пространство имен имело формально внешнюю связь, возможно, если только оно не находится внутри анонимного пространства имен (C ++ 03 §3.5 / 4 last dash + C ++ 03 §7.3.1.1 / 1). В C ++ 11 анонимное пространство имен имеет формально внутреннюю связь.
Это не имеет значения для компоновщика, потому что нет связи пространств имен, но это важно как формальное устройство для описания взаимосвязи вещей:
C ++ 11 §3.5 / 4:
» Пространство имен без имени или пространство имен, объявленное прямо или косвенно в пространстве имен без имени, имеет внутреннюю связь. Все остальные пространства имен имеют внешнюю связь. Имя, имеющее область имен, которое не имеет
указанная выше внутренняя связь имеет ту же связь, что и окружающее пространство имен, если это имя
— Переменная; или же
— функция; или же
— именованный класс (раздел 9) или безымянный класс, определенный вtypedef
объявление, в котором класс имеетtypedef
имя для целей связи (7.1.3); или же
— именованное перечисление (7.2) или неназванное перечисление, определенное вtypedef
объявление, в котором перечисление имеет имя typedef для целей связывания (7.1.3); или же
— перечислитель, принадлежащий перечислению со связью; или же
Шаблон.
Прежде чем перейти к другим вашим вопросам, стоит отметить, что эта цитата из стандарта,
» Хотя сущности в безымянном пространстве имен могут иметь внешнюю связь, они фактически квалифицируются по имени, уникальному для их единицы перевода, и, следовательно, никогда не будут видны из любой другой единицы перевода.
просто неправильно, потому что extern "C"
сущность видна из других единиц перевода независимо от того, в каком пространстве имен она объявлена.
К счастью, насколько я помню, примечания не являются нормативными, то есть они не определяют язык.
ре
» Как явно сделать внешнюю связь для имени в безымянном пространстве имен
просто объявить неconst
переменная или функция, как extern
,
Вы можете объявить неconst
переменная или функция, как extern "C"
делая связь внешней, но в то же время делая пространства имен неактуальными для связи: в языке C их нет.
namespace {
extern "C" void foo() {} // Extern linkage
} // namespace <anon>
void bar() {} // Also extern linkage, but visible to other TUs.
ре
» как проверить, что связь на самом деле внешняя
ну, связь влияет на такие вещи, как возможные конфликты с правилом единого определения, которое часто сокращается как «ODR”, Которая в C ++ 11 является §3.2.
Итак, один из способов увидеть связь в действии — это связать два объектных файла, сгенерированных из вышеуказанного источника, как если бы у вас было два модуля перевода с одним и тем же исходным кодом:
C: \ мои \ форумы \ так \ 088> g ++ -c anon.cpp -o x.o g ++ -c anon.cpp -o y.o C: \ мои \ форумы \ так \ 088> g ++ main.cpp x.o y.o y.o: anon.cpp :(. text + 0x0): множественное определение `foo ' x.o: anon.cpp :(. text + 0x0): сначала определено здесь y.o: anon.cpp :(. text + 0x7): множественное определение `bar () ' x.o: anon.cpp :(. text + 0x7): сначала определено здесь collect2.exe: ошибка: ld вернул 1 состояние выхода C: \ мои \ форумы \ так \ 088> _
Компоновщик жалуется на множественные определения foo
потому что, как и в случае с привязкой к языку C, для линкера это выглядит какinline
член внешней связи глобального пространства имен с двумя (возможно, конфликтующими) определениями.
Согласно стандарту [3.5 / 2]:
Когда имя имеет внешняя связь , объект, который он обозначает, может быть
упоминается по именам из областей других переводческих единиц или из
другие возможности того же переводчика
а также
Когда имя имеет внутренняя связь , объект, который он обозначает, может быть
упоминается по именам из других областей в той же единице перевода.
Таким образом, в основном, если вы можете ссылаться на что-то в единице перевода, отличной от той, в которой это что-то было определено, тогда это имеет внешнюю связь, в противном случае она имеет внутреннюю связь. Итак, учитывая примечание от вопроса:
Хотя сущности в безымянном пространстве имен могут иметь внешнюю связь,
они квалифицированы по имени, уникальному для их перевода
единица и, следовательно, никогда не будет видно из любой другой единицы перевода.
У нас практически есть ситуация, в которой у нас есть имя, но мы его не знаем, поэтому, как бы мы ни старались, мы не можем ссылаться на него из другого блока перевода. И это делает его неотличимым от того, что имеет внутреннюю связь. Так что, на мой взгляд, это всего лишь жонглирование словами — если вы не можете отличить одну ситуацию от другой, тогда они одинаковы.