我最近在一个头文件中看到了这段代码,并且惊讶于它的工作原理:
namespace NS {
static int uid = 0;
class X {
public:
static int getUID() { return uid++; }
};
}
Run Code Online (Sandbox Code Playgroud)
如果NS::X::getUID()从几个不同的C++源文件中调用静态方法,我很惊讶地发现它正确生成了一个唯一的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;
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的输出(在从main调用这些之后)是:
File1, UID1: 0, UID2: 1
File2, UID1: 2, UID2: 3
Run Code Online (Sandbox Code Playgroud)
Jam*_*lis 14
如果您只将此标头包含在单个源文件中,那么您的代码才是正确的.
因为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++;
}
};
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,保证程序中只有一个实例uid.