Confusion about declaration and definition of static const data memebers
no need to define integral static const data members in classes; declarations alone suffice,
Declarations alone suffice only if that object is not ODR-used, that is, if a data member is not used in a context that would require its address to exist (like binding to a reference or applying operator &
). The presence of an initializer does not equal a definition.
In the example from the book, it's clear that MinVals
is not ODR-used, i.e., the compiler can use its value directly, without having to create an object in memory, and so the statement:
widgetData.reserve(Widget::MinVals);
becomes:
widgetData.reserve(28);
If, however, in any other place, MinVals
were ODR-used, that would make the program ill-formed.
All other examples from cppreference clearly indicate when a value is ODR-used and a definition is required, and when not:
struct X
{
const static int n = 1;
const static int m{2}; // since C++11
const static int k;
};
const int X::k = 3;
n
and m
are declarations with initializers. An attempt to obtain the address of either n
or m
should fail.
struct X {
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m;
const int X::n;
constexpr int X::m;
Expressions &X::n
and &X::m
count as ODR-use of n
and m
, respectively (that is, an address is requested). For a constexpr
static data members, a definition was required prior to C++17. From C++17, static constexpr
data members are implicitly inline
, which means, no out-of-class definition is needed, as they are definitions themselves.
Looking at this Draft Standard, it appears that your example falls into a grey area. While there is no explicit mention of lines such as:
static const std::size_t MinVals = 28;
There is an example given which is very similar:
6.1 Declarations and definitions
...
2 A declaration is a definition unless
...
2.3 — it declares a non-inline static data member in a class definition
...
Example: All but one of the following are definitions:
int a; // defines a
extern const int c = 1; // defines c
...
The second example is close to your code, but with a significant difference in having the extern
qualifier. Also, note that the above states that a declaration is (by default) also a definition unless one of the listed conditions applies; I would say (though I'm no Language-Lawyer) that none of those conditions are met exactly in your case, so your declaration is also a definition.
NOTE: The linked document is only a draft standard; make sure to read the 'disclaimer' given at the foot of its first page!