When using g++ 3.4.6 (with ld 2.15.92.0.2), I can write:
class ConstantContainer {
public:
static const uint16_t MyConstant1 = UINT16_C(0x4321);
static const uint32_t MyConstant2 = UINT32_C(0x87654321);
static const uint64_t MyConstant3 = UINT64_C(0xF0E1D2C3B4A59687);
static const double MyConstant4 = 1.0 / 4.0;
};
and use ConstantContainer::MyConstant1
and others almost everywhere as a cheep substitute for scoped literals, with the exception of initializing other constants.
However, when using g++ 3.3.6 (with ld of the same version 2.15.92.0.2, although not the same binary and distro), the code compiles fine, too, but linking fails in some cases due to unresolved reference at any point a “constant” is used:
g++ -o myapp module1.o module2.o ... main.o
moduleN.o(.text+0x59e): In function `BlahBlah(const FooBar&)':
: undefined reference to `ConstantContainer::MyConstant1'
I could not figure out that are the unique features that provoke such a behavior. For example, a non-compatible case may be as simple as this:
class GraphConversionState {
public:
struct NodeIndex {
public:
typedef CxxStd::uint32_t ValueType;
ValueType Value;
class ValueSpecial {
public:
static CxxConstExpr ValueType
Unknown = UINT32_C(0xFF000000),
Isolated = UINT32_C(0xFF111111),
Connected = UINT32_C(0xFFCCCCCC);
};
};
};
I. e. there is only a small bunch of static constant members of uint type, yet they don't qualify to be treated as named literals; meanwhile, in other cases, even floating point values are fine. The only obvious difference is scope level (class nesting), but that doesn't prove to be the real reason in general case with simplified examples.
The obvious workaround is to turn the aforementioned class into a monster:
class ConstantContainerType {
public:
uint16_t MyConstant1;
uint32_t MyConstant2;
uint64_t MyConstant3;
double MyConstant4;
ConstantContainerType() :
MyConstant1(UINT16_C(0x4321)),
MyConstant2(UINT32_C(0x87654321))
MyConstant3(UINT64_C(0xF0E1D2C3B4A59687))
MyConstant4(1.0 / 4.0)
{ }
};
static const ConstantContainerType ConstantContainer;
// in ConstantContainer.cpp:
const ConstantContainerType ConstantContainer;
But that is quite ugly, less clean and much more error-prone, as number of constants and container classes is high. Even more so, while in-place declared and defined constants are probably optimized as they were real literals, it is very doubtful they would be treated so when being a part of a singleton object.
So I wondered: what are the exact rules employed by GCC 3.3 and above for treating some static const POD declarations as constant definitions?