回覆
”在哪些情况下为未命名命名空间中的名称进行显式外部链接是有用的?
外部链接的需求对于 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 最后破折号 + 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
变量或函数, as 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:\my\forums\so\088> g++ -c anon.cpp -o xo & g++ -c anon.cpp -o yo
C:\my\forums\so\088> g++ main.cpp xo yo
yo:anon.cpp:(.text+0x0): 'foo' 的多重定义
xo:anon.cpp:(.text+0x0): 首先定义在这里
yo:anon.cpp:(.text+0x7): `bar()' 的多重定义
xo:anon.cpp:(.text+0x7): 首先在这里定义
collect2.exe:错误:ld 返回 1 退出状态
C:\my\forums\so\088> _
链接器抱怨 的多个定义foo
,因为与 C 语言绑定一样,就链接器而言,它显示为inline
全局命名空间的非外部链接成员,具有两个(可能是冲突的)定义。