在具有外部链接的匿名命名空间中声明的实体的示例

Example of entity declared in a anonymous namespace that has external linkage

本文关键字:声明 实体 命名空间 外部 链接      更新时间:2023-10-16

给定§3.5/4中的以下语句(强调mine)和§7.3.1.1/1中的注释[94],我希望有一个在具有外部链接的未命名命名空间中声明的实体的示例。

§3.5/4

未命名的命名空间或直接或间接声明的命名空间在未命名的命名空间中具有内部链接。所有其他命名空间具有外部链接名称空间作用域尚未上面给定的内部链接与封闭的链接相同命名空间,如果它是的名称

  • 变量;或
  • 函数;或
  • 命名类(第9条),或在typedef声明中定义的未命名类,在该声明中该类具有用于链接的typedef名称目的(7.1.3);或
  • 命名枚举(7.2),或在typedef声明中定义的未命名枚举,其中枚举具有的typedef名称链接目的(7.1.3);或
  • 属于具有链接的枚举的枚举器;或
  • 模板

§7.3.1.1/1注释[94]:

尽管未命名名称空间中的实体可能具有外部链接,他们实际上是由他们翻译的唯一名称限定的单位,因此从任何其他翻译单位都看不到。

您看到的是标准中的一个缺陷。

使未命名名称空间成员具有内部链接的更改发生在2010年11月C++11标准化过程的相当晚的时候(CWG第1113期)。因此,标准中的许多地方需要更改,但没有。其中之一就是你引用的脚注。

CWG第1603期目前处于"准备就绪"状态(内容如下:该决议可能会在下一次委员会会议上通过),将解决这一问题以及与未命名命名空间成员的内部链接相关的其他一些问题。

这是一个很好的问题,因为它很难演示。我们可以利用C++标准中的其他规则来表明匿名命名空间中的变量可以具有外部链接。

对具有外部链接的int*进行模板化将成功,而对具有内部链接的int*进行模板化则将失败。

#include <iostream>
namespace {
    // not externally linked, won't compile
    // const int i = 5;
    // external linkage, compiles
    extern int i;
    int i = 5;
}
template<int* int_ptr>
struct temp_on_extern_linked_int {
    temp_on_extern_linked_int() {
        std::cout << *int_ptr << std::endl;
    }
};
int main() {
    temp_on_extern_linked_int<&i>();
}

如图所示,程序编译并运行。

$ g++-4.8 main.cpp -o main
$ ./main
5

取消对i的其他定义的注释会导致编译失败。

$ g++-4.8 main.cpp -o main
main.cpp: In function 'int main()':
main.cpp:17:30: error: '& {anonymous}::i' is not a valid template argument of
type 'int*' because '{anonymous}::i' does not have external linkage
  temp_on_extern_linked_int<&i>();
                              ^

编译器非常有用。它明确指出,由于i没有外部链接,因此编译失败。

i的注释定义具有内部链接,因为它是不带外部的限定常量。(§3.4.6)

Variables at namespace scope that are declared const and not extern have internal linkage.

技巧的一部分是不要编译为C++11。

为什么C++03要求模板参数具有外部链接?

例如

#include <iostream>
namespace
{
    extern int x = 10;
    void f( int y )
    {
        extern int x;
        std::cout << x + y << std::endl;
    }
}
int main() 
{
    int y =  15;
    f( y );
    return 0;
}

根据C++标准

6块范围中声明的函数的名称和块作用域外部声明声明的变量具有链接。如果具有链接的实体的可见声明具有相同的名称和类型,忽略在最内层外部声明的实体封闭命名空间范围,块范围声明声明同一实体,并接收先前声明的链接。如果存在不止一个这样的匹配实体,程序不正规。否则,如果未找到匹配的实体,则块作用域实体接收外部链接