I need to define a C++ class that generate unique identifiers. The idea is that every instance of every visible class will be tagged by a unique integer value. Let's call that the "handle" of the instance. The handles must be unique across all the instances in the system.
Some instances may be related to other ones, in which case their class defines a member to store the handle of their relative.
But not all instances actually have a relative. So I need a special handle value to stand for an "undefined" relative.
I need help to define that special instance. Here is what I tried:
class Handle {
public:
Handle(): mValue(newValue()) {};
static const Handle kUndefHandle(0); // (1)
protected:
Handle(int val): mValue(val) {}; // (2)
int mValue;
static int mNextValue = 1000; // start at 1000. (3)
static int newValue() { return mNextValue++; }
};
Notes:
line (3) doesn't compile (Clang 3.x). The compiler refuses the inline initialization of the static variable. The fix is easy, initialize it offline in an implementation file. So my class cannot be header-only, but I can live with that.
line (1) defines my special constant instance. Unfortunately, the compiler fails, saying "Expected parameter declarator".
I also tried: static const Handle kUndefHandle = 0;
but then the compiler complains "Variable has incomplete type 'Handle'", even if I put it in a subclass. And I can't reopen a class in C++ like in Ruby.
I can sort of make it work by putting that const instance declaration outside of the class. I lose the class scope, but that's a minor drawback. I can still use a namespace if I care.
However this only works if I make the constructor in line (2) public. And I don't want that. I don't want to let the programmer construct handles with arbitrary values.
Any suggestion?
This works for me:
class Handle {
public:
Handle(): mValue(newValue()) {};
static const Handle kUndefHandle;
protected:
Handle(int val): mValue(val) {};
int mValue;
static int mNextValue;
static int newValue() { return mNextValue++; }
};
const Handle Handle::kUndefHandle(0);
int Handle::mNextValue = 1000;
Initializing a static class member is actually considered to be within the scope of the class, so you can access private and protected constructors in that context.
Note that you should make the constructor private, because there currently exists a loophole by which people could construct handles with arbitrary values: derive the class, chain to the protected constructor, then convert the resultant object to Handle
. (See an example on ideone)
class FakeHandle : public Handle
{
public:
FakeHandle(int val) : Handle(val) { }
};
Now one could do:
Handle badHandle = FakeHandle(5);