namespace NS { static int uid = 0; class X { public: static int getUID() { return uid++; } }; }
如果从几个不同的C源文件调用静态方法NS :: X :: getUID(),我很惊讶地发现它正确地生成了唯一的ID(跨翻译单元).我认为命名空间范围中的一个静态变量与翻译单元有内部链接.这里发生了什么?类X中的内联静态方法是否具有自己的翻译单元,这就是为什么它生成唯一的ID?或者是因为我的编译器有点怪怪,这对我来说有用吗?
上述代码是否依赖于“安全”的明确行为?如果是这样,这是一个令人惊讶的简洁的方法来生成内联或模板类中的唯一ID,即使它看起来有点kludgy.或者更好地为这样的静态唯一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; }
File1,UID1: 0,UID2: 1 File2,UID1: 2,UID2: 3
解决方法
因为uid被声明为静态,它具有内部链接.每个源文件都有一个uid变量的实例,其中包含此头.如果将这个头文件包含在三个源文件中,那么会有三个uid变量.
getUid函数是隐式内联的,因为它定义在类定义的内部.它是静态成员函数的事实是无关紧要的.内联函数的规则是必须在其使用的每个源文件中定义内联函数,并且所有定义必须相同.
您的getUid函数定义违反了这个规则:是的,它在每个包含头文件的源文件中定义(因为它在头中定义),但每个定义是不同的,因为在每个定义中,引用的uid是一个不同的uid变量.
因此,您的程序违反“一维规则”,并表现出未定义的行为.您观察到的特定行为可能是因为编译器选择了内联函数的一个定义,并抛弃了其余的内容,所以您认为您正在使用的“全局变量”恰好恰好是uid变量之一 – 无论哪个被引用通过编译器保存的getUid的副本.虽然这是这种形式的未定义行为的典型表现,但行为仍未定义.
您可以使uid变量function-local来确保只有一个实例,并避免违反一个定义规则:
namespace NS { class X { public: static int getUID() { static int uid = 0; return uid++; } }; }
在这种情况下,在程序中将只有一个uid实例被保证.