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!