繁体   English   中英

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

[英]External linkage for name inside unnamed namespace

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

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

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

虽然未命名的命名空间中的实体可能具有外部链接,但它们实际上由其翻译单元唯一的名称限定,因此永远不会从任何其他翻译单元中看到。

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

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

回覆

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

对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中,匿名命名空间具有正式的内部链接。

这与链接器无关,因为没有名称空间的链接,但它是描述事物链接的正式设备:

未命名的命名空间或直接或间接在未命名的命名空间内声明的命名空间具有内部链接。 所有其他名称空间都有外部链接。 具有名称空间作用域的名称上面没有给出内部链接,如果是名称,则具有与封闭名称空间相同的链接
- 一个变量; 要么
- 一个功能; 要么
- 命名类(第9节),或在typedef声明中定义的未命名类,其中类具有用于链接目的的typedef名称(7.1.3); 要么
- 命名枚举(7.2),或在typedef声明中定义的未命名枚举,其中枚举具有用于链接目的的typedef名称(7.1.3); 要么
- 属于具有链接的枚举的枚举器; 要么
- 一个模板。


在继续你的其他问题之前,值得注意的是这个引用来自标准,

尽管未命名的命名空间中的实体可能具有外部链接,但它们通过其翻译单元特有的名称进行有效限定,因此永远不会从任何其他翻译单元中看到。

是完全错误的 ,因为从其他翻译单元可以看到extern "C"实体,无论它声明了什么名称空间。

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


回覆

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

只是声明一个非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. 

回覆

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

好吧,这种联系会影响可能与一个定义规则发生冲突的事情,通常缩写为“ ODR ”,在C ++ 11中是§3.2。

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

\n C:\\ my \\ forums \\ so \\ 088> \n\n C:\\ my \\ forums \\ so \\ 088> \n 哟:anon.cpp :(。text + 0x0):`foo'的多重定义\n xo:anon.cpp :(。text + 0x0):首先在这里定义\n 哟:anon.cpp :(。text + 0x7):`bar()'的多重定义\n xo:anon.cpp :(。text + 0x7):首先在这里定义\n collect2.exe:错误:ld返回1退出状态\n\n C:\\ my \\ forums \\ so \\ 088> _\n

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

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

我能想到的唯一方法是给它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删除) )。

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

我想不出任何有用的情况。

根据标准[3.5 / 2]:

当名称具有外部链接时 ,其表示的实体可以通过其他翻译单元的范围或同一翻译单元的其他范围中的名称来引用

当名称具有内部链接时 ,其表示的实体可以通过同一翻译单元中其他范围的名称来引用。

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

虽然未命名的命名空间中的实体可能具有外部链接,但它们实际上由其翻译单元唯一的名称限定,因此永远不会从任何其他翻译单元中看到。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM