'Confusion about declaration and definition of static const data memebers
Scott Meyers writes in Effective Modern C++ (Item 30 page 210) that there's
no need to define integral
static const
data members in classes; declarations alone suffice,
then the sample code is
class Widget {
public:
static const std::size_t MinVals = 28; // MinVals' declaration;
...
};
... // no defn. for MinVals
std::vector<int> widgetData;
widgetData.reserve(Widget::MinVals); // use of MinVals
I was convinced that static const std::size_t MinVals = 28;
is declaration and also a definition, as it is giving a value to MinVals
, but the comment seems to claim that's only a declaration; the second comment actually claims there's no definition. The text after the code, indeed reads
MinVals
lacks a definition.
Which confirms that static const std::size_t MinVals = 28;
is not a definition, so I'm a bit confused.
cppreference doesn't help me much (my bold-italic):
If a
static
data member of integral or enumeration type is declaredconst
(and notvolatile
), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:struct X { const static int n = 1; const static int m{2}; // since C++11 const static int k; }; const int X::k = 3;
but first two lines in the class look definitions to me.
The same goes for a following example on cppreference:
struct X { static const int n = 1; static constexpr int m = 4; }; const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used const int X::n; // … so a definition is necessary constexpr int X::m; // … (except for X::m in C++17)
where I'd have said static const int n = 1;
is a definition, but it is not, based on the second to last comment.
Solution 1:[1]
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.
Solution 2:[2]
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!
Solution 3:[3]
From The Standard Chapter "12.2.3.2 Static data members":
The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
By using it, it shall be defined.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Piotr Skotnicki |
Solution 2 | |
Solution 3 | Dragan |