未命名命名空间内名称的外部链接

External linkage for name inside unnamed namespace

根据 C++ 标准的条款 3.5/4:

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage.

同时在第 7.3.1.1 段中我们有注释 96):

Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit.

如果标准保证无法从另一个翻译单元访问未命名命名空间内定义的名称,如何在未命名命名空间内为名称显式建立外部链接以及如何检查链接实际上是外部链接?

在哪些情况下对未命名命名空间内的名称进行显式外部链接有用?

回复

In which cases doing explicit external linkage for name inside unnamed namespace is useful?

外部 linkage 的需要对于 C++03 模板很重要。例如。作为模板参数的函数指针必须是指向外部 linkage 函数的指针。例如,以下不会用 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,具有外部 linkage 但翻译单元之间没有名称冲突的匿名命名空间机制仅在技术上对 classes 是必需的。 class 具有外部 link 年龄。人们不想选择 class 保证不会出现在其他翻译单元中的名称。

使用 C++11 规则发生了变化 不仅是模板参数,还有匿名命名空间中的事物是否具有外部 linkage。对于 C++03,匿名命名空间具有正式的外部 linkage,可能除非它本身位于匿名命名空间中(C++03 §3.5/4 最后一个破折号 + C++03 §7.3.1.1/1) .在 C++11 中,匿名命名空间具有正式的内部 linkage.

这对 link 人来说无关紧要,因为没有 link 命名空间,但它作为描述 link 事物时代的正式设备很重要:

C++11 §3.5/4:

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
— a variable; or
— a function; or
— a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3); or
— a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3); or
— an enumerator belonging to an enumeration with linkage; or
— a template.


在继续回答您的其他问题之前,值得注意的是标准中的这句话,

Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit.

显然是 错误的 ,因为 extern "C" 实体在其他翻译单元中是可见的,无论它在哪个命名空间中声明。

令人高兴的是,我记得,注释是非规范的,即它们不定义语言。


回复

How to explicitly make external linkage for name inside unnamed namespace

只需声明一个非const变量,或一个函数,如extern. 您可以将非 const 变量或函数声明为 extern "C",使 linkage extern 但同时使名称空间与 link 无关ing方面:C语言没有。

namespace {
    extern "C" void foo() {}    // Extern linkage
}  // namespace <anon>

void bar() {}  // Also extern linkage, but visible to other TUs.

回复

how to check that linkage is actually external

好吧,link年龄会影响一些事情,比如可能与单一定义规则发生冲突,通常缩写为“ODR”,在 C++11 中是§3.2.

因此,查看 linkage 的一种方法是 link 从上述源代码生成的两个目标文件,就像您有两个具有相同源代码的翻译单元一样:

C:\my\forums\so8> g++ -c anon.cpp -o x.o & g++ -c anon.cpp -o y.o

C:\my\forums\so8> g++ main.cpp x.o y.o
y.o:anon.cpp:(.text+0x0): multiple definition of `foo'
x.o:anon.cpp:(.text+0x0): first defined here
y.o:anon.cpp:(.text+0x7): multiple definition of `bar()'
x.o:anon.cpp:(.text+0x7): first defined here
collect2.exe: error: ld returned 1 exit status

C:\my\forums\so8> _

linker 抱怨 foo 的多个定义,因为对于 C 语言绑定,就 linker 而言,它似乎是一个非 inline 全局命名空间的外部 linkage 成员,具有两个(可能相互冲突的)定义。

按照标准[3.5/2]:

When a name has external linkage , the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit

When a name has internal linkage , the entity it denotes can be referred to by names from other scopes in the same translation unit.

所以基本上,如果您可以在翻译单元中引用与定义此内容的翻译单元不同的内容,则它具有外部链接,否则它具有内部链接。所以给出问题的注释:

Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit.

我们实际上有一种情况,我们有一个名字但我们不知道它,因此无论我们多么努力,我们都无法从不同的翻译单位引用它。它使它与具有内部链接的无法区分。所以,在我看来,这只是一个词的变戏法——如果你不能区分一种情况和另一种情况,那么它们是一样的。

How to explicitly make external linkage for name inside unnamed namespace

我能想到的唯一办法就是给它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 应该有内部链接,但这不是编译器似乎做的。)

and how to check that linkage is actually external if Standard guaranteed that there is no way to access name defined inside unnamed namespace from another translation unit?

通常 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 的遗留问题,并且应该更正或删除该注释(实际上 T.C。指出它是已被 DR 1603).

删除

In which cases doing explicit external linkage for name inside unnamed namespace is useful?

我想不出它有什么用处。