【问题标题】:Redefinition member of the namespace into the nested inline namespace将命名空间的成员重新定义为嵌套的内联命名空间
【发布时间】:2014-06-14 12:53:36
【问题描述】:

N3797 的 7.3.1/8 有一段引述:

内联命名空间的成员在大多数情况下都可以像使用 它们是封闭命名空间的成员。

考虑以下代码sn-p:

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

我认为该示例违反了ODR。但事实并非如此,它正在编译成功。你能解释一下这种行为吗?

【问题讨论】:

    标签: c++ namespaces inline language-lawyer


    【解决方案1】:

    简介

    7.3p1 命名空间 [basic.namespace]

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

    命名空间内声明的实体属于该命名空间,即。它是该特定命名空间的成员,无论命名空间是否内联


    ODR 违规 = N0NE

    您的示例 sn-p 违反 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]

    具体来说,内联命名空间和它的封闭命名空间都被添加到参数相关的 lokoup (3.4.2) 中使用的关联命名空间集合中,只要它们中的一个是,以及命名的 using-direction (7.3.4)内联命名空间隐式插入到封闭的命名空间中,就像未命名的命名空间(7.3.1.1)一样。

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

    添加的名称不被视为对先前声明的实体的重新声明,它们是嵌套声明区域中的附加名称,在名称查找期间被带入封闭的命名空间。


    注意:依靠编译器发出 ODR 违规的诊断是不安全的,主要是因为标准明确规定 “不需要诊断 [is] " 如果应用程序违反了 [basic.def.odr]. 设置的规则

    Matthieu M. 对此帖子的评论中提供了更多详细信息。

    【讨论】:

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