I saw this code recently in a header file, and was surprised that it worked:
namespace NS {
static int uid = 0;
class X {
public:
static int getUID() { return uid++; }
};
}
If the static method NS::X::getUID()
is called from several different C++ source files, I was surprised to find that it correctly generated a unique ID (unique across translation units). I thought that a static variable in a namespace scope had internal linkage to the translation unit. What's going on here? Does the inline static method in class X have it's own translation unit, and that's why it generates a unique ID? Or is it working for me due to a quirk in my compiler?
Does the above code rely on "safe" well-defined behavior? If so, this is a surprisingly concise method of generating a Unique ID in an inline or template class, even if it looks a bit kludgy. Or is it better to generate a new C++ source file for a static unique ID function like this, and move the static ID inside the class?
Update:
For a test case, I wrote several functions like this in different files (file1.cpp, file2.cpp, etc.):
#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;
}
The suprising output (after calling these from main) was:
File1, UID1: 0, UID2: 1
File2, UID1: 2, UID2: 3
Static local variables are not allowed to be defined within the body of an inline function. C++ functions implemented inside of a class declaration are automatically defined inline.
From outside the class, "static variables should be accessed by calling with class name." From the inside, the class qualification is inferred by the compiler.
A static variable is a variable that is declared using the keyword static. The space for the static variable is allocated only one time and this is used for the entirety of the program. Once this variable is declared, it exists till the program executes.
In computer programming, a static variable is a variable that has been allocated "statically", meaning that its lifetime (or "extent") is the entire run of the program.
Your code is only correct if you only ever include this header in a single source file.
Because uid
is declared as static
, it has internal linkage. There is one instance of the uid
variable per source file in which this header is included. If you include this header in three source files, there will be three uid
variables.
The getUid
function is implicitly inline
because it is defined inside of the class definition. The fact that it is a static
member function is irrelevant. The rule for inline
functions is that an inline
function must be defined in every source file in which it is used, and that all of the definitions must be identical.
Your getUid
function definition violates this rule: yes, it is defined in every source file that includes the header (because it is defined in the header), but each definition is different, because in each definition the uid
referred to is a different uid
variable.
Therefore, your program violates the One Definition Rule and exhibits undefined behavior. The particular behavior you observe is likely because the compiler picks one definition of the inline function and just discards the rest, so the "global variable" that you think you are using just happens to be one of the uid
variables--whichever one was referenced by the copy of getUid
that the compiler kept. While this is a typical manifestation of this form of undefined behavior, the behavior is nonetheless undefined.
You can make the uid
variable function-local to ensure that there is exactly one instance, and to avoid violating the One Definition Rule:
namespace NS {
class X {
public:
static int getUID() {
static int uid = 0;
return uid++;
}
};
}
It is guaranteed in this case that there will be exactly one instance of uid
in the program.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With