将命名空间的重新定义成嵌套的内联命名空间成员。

5

来自N3797的7.3.1/8的一句话引用如下:

内联命名空间的成员可以在大多数情况下被视为是封闭命名空间的成员。

考虑以下代码片段:

namespace M
{
    int j = 7;
    inline namespace MM
    {
        int j = 8;
    }
}

我认为这个例子违反了ODR。但实际上它是正确编译的。你能解释一下这种行为吗?

1个回答

3

介绍

7.3p1 命名空间 [basic.namespace]

命名空间是一个可选命名的声明性区域。命名空间的名称可用于访问在该命名空间中声明的实体,即命名空间的成员。与其他声明性区域不同,命名空间的定义可以分布在一个或多个翻译单元的几个部分中。

在命名空间内声明的实体属于该命名空间,即使命名空间是内联的也是如此。


ODR违规 =

您的示例片段不会违反ODR,主要是因为您有2个名为j不同实体;

namespace N {
  int j = 0;            // 1st

  inline namespace M {
    int j = 1;          // 2nd
  }
}

正如在[namespace.def]p8中进一步指出的那样,在封闭命名空间中进行名称查找将包括任何inline命名空间中发现的名称,但嵌套内联命名空间的成员仍然是它们自己的实体。

7.3.1p8 命名空间定义 [namespace.def]

具体来说,每当其中一个命名空间被添加到关联命名空间集合中时(用于参数相关查找(3.4.2)),内联命名空间及其封闭命名空间都会被添加到关联命名空间集合中。并且一个使用内联命名空间的using-direction(7.3.4)将被隐式插入到封闭命名空间中,就像对于未命名命名空间一样(7.3.1.1)。

此外,内联命名空间的每个成员都可以随后明确实例化(14.7.2)或明确专门化(14.7.3),就像它是封闭命名空间的成员一样。最后,通过显式限定符(3.4.3.2)在封闭命名空间中查找名称将包括由using-directive引入的内联命名空间的成员,即使在封闭命名空间中有名称的声明。

添加的名称不被视为先前声明实体的重新声明,它们是嵌套声明区域中的附加名称,在名称查找期间将其引入封闭命名空间。
注意:依赖编译器以ODR违规的方式发出诊断并不安全,主要是因为标准明确指出,如果应用程序违反[basic.def.odr]设置的规则,则“不需要诊断”。有关更多详细信息,请参见Matthieu M.在此帖子上的评论。

具体来说(针对注释):在跨翻译单元检测具有相同名称但不同定义的多个实体是很困难的。编译器本身通常只能一次看到一个翻译单元,因此诊断落在链接器上。链接器将为常规符号诊断重复符号,但是inline函数(因此template函数)是弱符号,链接器通常只选择一个并忽略其他符号...这可能会随着模块的改变而改变。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接