未命名的命名空间

时间:2016-02-09 09:54:35

标签: c++ c++14 unnamed-namespace

根据C ++标准第3.5 / 4条:

  

直接或间接声明的未命名命名空间或命名空间   在未命名的命名空间内有内部链接。

在第7.3.1.1段中,我们注意到96):

  

虽然未命名的命名空间中的实体可能具有外部链接,   他们的翻译专有名称有效   单位,因此永远不会从任何其他翻译单位看到。

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

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

3 个答案:

答案 0 :(得分:3)

RE

  

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

对于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,用于具有外部链接但在翻译单元之间没有名称冲突的匿名命名空间机制仅在技术上对于类是必需的。一个类有外部链接。人们不希望选择保证在其他翻译单元中没有出现的类名。

使用C ++ 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"实体在其他翻译单元中是可见的,无论其声明的名称空间是什么。

令人高兴的是,我记得,笔记是非规范性的,即他们没有定义语言。

RE

  

如何在未命名的命名空间中明确地为名称进行外部链接

只需将非const变量或函数声明为extern 你可以将非const变量或函数声明为extern "C",使得链接为extern,但同时使命名空间与链接无关:C语言不拥有它们。

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

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

RE

  

如何检查该链接实际上是外部的

好吧,链接会影响可能与One Definition Rule的冲突,通常缩写为“ ODR ”,在C ++ 11中是§3.2。

因此,查看链接的一种方法是链接从上面的源生成的两个目标文件,就好像你有两个具有相同源代码的翻译单元:

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

C:\my\forums\so\088> 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\so\088> _

链接器抱怨foo的多个定义,因为就C语言绑定而言,就链接器而言,它似乎是全局命名空间的非inline外部链接成员,有两个(可能是冲突的)定义。

答案 1 :(得分:2)

  

如何在未命名的命名空间中明确地为名称进行外部链接

我能想到的唯一方法是给它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的剩余部分,并且应该更正或删除该注释(事实上,TC指出它已经被删除了DR 1603)。

  

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

我无法想到任何有用的案例。

答案 2 :(得分:1)

根据标准[3.5 / 2]:

  

当名称具有外部链接时,它表示的实体可以是   来自其他翻译单位范围的名称或来自   同一翻译单位的其他范围

  

当名称具有内部链接时,它表示的实体可以是   由同一翻译单位中其他范围的名称引用。

所以基本上如果你可以在翻译单元中引用一些不同于已经定义了这个东西的翻译单元那么它有外部链接,否则它有内部链接。所以给出了问题的注释:

  

虽然未命名的命名空间中的实体可能具有外部链接,   他们的翻译专有名称有效   单位,因此永远不会从任何其他翻译单位看到。

我们实际上有一种情况,我们有一个名字,但我们不知道它,因此无论我们如何努力,我们都不能从不同的翻译单位引用它。它使它与具有内部联系的那个难以区分。因此,在我看来,这只是一个杂耍的词 - 如果你无法区分一种情况,那么它们是相同的。