繁体   English   中英

为什么从内联类方法中访问静态命名空间变量有效?

[英]Why does a static namespace variable accessed from inside an inline class method work?

我最近在一个头文件中看到了这段代码,并且惊讶于它的工作原理:

namespace NS {
  static int uid = 0;
  class X {
   public:
    static int getUID() { return uid++; }
  };
}

如果从几个不同的C ++源文件中调用静态方法NS::X::getUID() ,我很惊讶地发现它正确生成了一个唯一的ID(在翻译单元中是唯一的)。 我认为命名空间范围中的静态变量与翻译单元有内部链接。 这里发生了什么? X类中的内联静态方法是否具有自己的转换单元,这就是它生成唯一ID的原因? 或者由于编译器的怪癖,它是否适合我?

上面的代码是否依赖于“安全”明确定义的行为? 如果是这样,这是一种在内联或模板类中生成唯一ID的令人惊讶的简洁方法,即使它看起来有点笨拙。 或者为这样的静态唯一ID函数生成新的C ++源文件更好,并在类中移动静态ID?

更新:

对于测试用例,我在不同的文件(file1.cpp,file2.cpp等)中写了几个这样的函数:

#include "static_def.h" // Name of the above header file.
void func1() {
  int uid1 = NS::X::getUID();
  int uid2 = NS::X::getUID();
  std::cout << "File1, UID1: " << uid1 << ", UID2: " << uid2 << std::endl;
}

令人惊讶的输出(在从main调用这些之后)是:

File1, UID1: 0, UID2: 1
File2, UID1: 2, UID2: 3

如果您只将此标头包含在单个源文件中,则您的代码才是正确的。

因为uid被声明为static ,所以它具有内部链接。 每个源文件都有一个uid变量实例,其中包含此标头。 如果在三个源文件中包含此标头,则会有三个uid变量。

getUid函数是隐式inline因为它是在类定义中定义的。 它是static成员函数的事实是无关紧要的。 inline函数的规则是必须在使用它的每个源文件中定义inline函数,并且所有定义必须相同。

getUid函数定义违反此规则:是的,它在包括报头(因为它是在报头中定义的)每个源文件被限定,但各定义是不同的,因为在每一个定义的uid称为是一个不同的uid可变。

因此,您的程序违反了One Definition Rule并显示未定义的行为。 你观察到的特定行为可能是因为编译器采内联函数的一个定义,只是放弃休息,所以“全局变量”,你认为你正在使用恰好是一个 uid变量-哪一个被引用由编译器保存的getUid副本。 虽然这是这种形式的未定义行为的典型表现,但行为仍未定义。

您可以使uid变量function-local以确保只有一个实例,并避免违反One Definition Rule:

namespace NS {
  class X {
  public:
    static int getUID() {
      static int uid = 0;
      return uid++;
    }
  };
}

在这种情况下,保证程序中只有一个uid实例。

我相信你的编译器会通过不内联 getUID()方法来欺骗你。 getUID()隐式内联的事实并不意味着实际内联 - 它只是对编译器的推荐。

除此之外,你getUID() :UID有内部链接, getUID()也可以返回非唯一ID。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM