名称空间具有未命名的名称空间,声明了相同的变量

namespaces having unnamed namespace with the same variable declared

本文关键字:空间 变量 未命名 声明      更新时间:2023-10-16

我尝试了下面的虚拟代码来测试未命名的命名空间。

我有以下输出

 ctor 1
 ctor 0
 3
 5

我对这个有点困惑。

  1. 我期待编译器的错误,说它无法解决关于a::m_a的歧义。相反,它总是指向更少的嵌套。总是这样吗?c++遵循什么规则?
  2. 似乎编译器按照顺序创建变量CMyObj写在文件上。总是这样吗?
  3. 是否有任何方法可以访问最嵌套的m_a变量源自main() ?
class CMyObj{     
    public:
    CMyObj(int a){std::cout  << "ctor " << a << std::endl; }
 };
 namespace a{ 
      namespace{
           int m_a=4;
           int m_b=5;
           CMyObj m_obj(1);
      }  
 }
 namespace a{
      int m_a=3;
      CMyObj m_obj(0);
 }
 int main(){
      std::cout << a::m_a << std::endl; // which one?
      std::cout << a::m_b << std::endl; // how this is possible?
      return 0;
 }

我没有c++ 03标准来检查那里的措辞,所以我将引用FDIS n3290。我认为这个问题的答案可以在3.4.3.2/2中的限定名称查找规则中找到:

对于命名空间X和名称m,命名空间限定的查找集S(X,m)定义如下:设S0(X,m)是X中所有m声明的集合和X的内联命名空间集合(7.3.1)。如果S0(X,m)不为空,则S(X,m)为S0(X,m);否则,S(X,m)是S(Ni,m)对于X及其内联命名空间集中使用指令指定的所有命名空间Ni的并集。

现在,请记住,未命名的命名空间是带有using指令的唯一命名空间。

首先看一下这个简化的代码(以及我的简化解释,您可以阅读§3.4.3.2了解详细信息):

namespace a
{
    int x;
}
int main()
{
    int i = a::x;
}

考虑当我们说a::x时会发生什么。首先,编译器枚举ax的所有声明。如果它找到一个明确的x,它就成功完成。否则,它会递归地搜索using指令声明的名称空间。如果它从未找到结果,则程序是病态的。

namespace a
{
    int x;
}
namespace b
{
    using namespace a;
}
int main()
{
    int i = b::x;
}

在这里,它没有在b中找到x,所以它搜索名称空间a(因为using-directive)并找到它。现在应该可以理解为什么这不是二义性了:

namespace a
{
    int x;
}
namespace b
{
    using namespace a;
    int x;
}
int main()
{
    int i = b::x;
}

这里,它在b中找到x,而从不考虑a。现在考虑一下,未命名的名称空间实际上只是一个具有唯一未知名称的名称空间:

namespace b
{
    namespace
    {
        int x;
    }
    // this is what an unnamed namespace expands to (in exposition)
    namespace __unique__ {}
    using namespace __unique__;
    namespace __unique__
    {
        int x;
    }
    int x;
}
int main()
{
    int i = b::x;
}

和前面一样,在b中找到x而不考虑未命名的名称空间。你的代码是类似的

我应该花时间在规范中找到确切的定义,但是当您有一个匿名(未命名)名称空间时,编译器实际上会生成一个混乱的名称。当你写

a::m_b 

在第二个std::cout语句中,编译器会自动替换被修改过的名称,以便您可以访问它。引用Gene Bushuyev随后的回答:

现在,请记住,未命名的名称空间是唯一命名的名称空间

在名字冲突的情况下,编译器知道a::m_a的意思,所以它使用它。它位于名称空间的顶层。我认为此时没有任何方法可以获得m_a的未命名命名空间副本。

这个页面很好地解释了名称空间。Winterdom: c++命名空间

没有歧义,因为namespace::<unnamed>::m_a的作用域是外部命名空间(namespace::a)。没有办法在main函数中访问namespace::<unnamed>::m_a,这就是为什么没有歧义。尝试编译以下代码,您将得到错误:

namespace ns{
  namespace {
    int a = 2;
  }
  int a = 3;
  int c = a;
}

位于同一翻译单元中的全局变量将按照声明的顺序初始化。在不同翻译单元中声明的全局变量的初始化顺序未定义