Confusion about declaration and definition of static const data memebers
Asked Answered
G

3

28

Scott Meyers writes in Effective Modern C++ (Item 30, at 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 declared const (and not volatile), 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.

Gassing answered 17/5, 2020 at 18:11 Comment(5)
From n4835.pdf [class.static.data] (2) The declaration of a non-inline static data member in its class definition is not a definition [...]. But (3) is directly for integral types with an initializer and this paragraph does not help if this is a declaration or definition.Ann
Thinking at it again, @AndyG, I don't there's something else to explicitly state to make my question clear. The title begins as Confusion about. That's it. I'm confused about that and I asking for help. Confusion about [...]. Can you help me understand this?Gassing
C++17 introduces inline static, and it's awesome.Neutrophil
recent, relatedSheri
In hindsight, this is also related.Gassing
T
17

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.

Traumatism answered 17/5, 2020 at 18:59 Comment(5)
The critical phrase here is initializing declaration.Downdraft
Answer accepted, but I have two doubts about the very first paragraph in your answer. You write Declarations alone suffice only if [...]; do you refer specifically to declarations of _ integral static const data members_, or to declarations in general? You finish the paragraph with The presence of an initializer does not equal a definition, which makes it look like a consequence of what precedes it, but I don't see the connection. Maybe you can clarify that first paragraph?Gassing
That depends on what kind of declarations you ask about. You can feel free not to define a static data member (either const or not), and only declare it as long as you don't ODR-use it. You can feel free not to define a function, as long as it's not called. You can feel free not to define a class, as long as you do not use it in a context that requires its full definition to exist. You are allowed to put an initializer to integral const static data members, but it doesn't make it a definition. It's valid, and allows the compiler to perform optimization, as long as that entity is not ODR-used.Traumatism
Don't focus only on the syntax. static const int i = 0; means something different whether it's in a class scope or in the global scope. It's semantics what eventually matters, and it results from both syntax and context.Traumatism
And the standard makes it explicitly a non-definition in [basic.def]/p2.3, unless you put also the inline keyword.Traumatism
T
3

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!

Thymol answered 17/5, 2020 at 18:36 Comment(5)
But the /2.3 condition does apply here!Downdraft
@DavisHerring That's my 'grey area'. I think it applied literally but the term 'inline' can be ambiguous: i.e. either an explicit inline qualifier or 'in body' (code placed inside the class definition). I'm probably only adding to the confusion, though.Thymol
@AdrianMole, since inline in 2.3 is not code-formatted, I agree with you that non-inline means not written inside the class definition, which would mean that 2.3 doesn't apply to my (well, Scott Meyers' book's) case.Gassing
@EnricoMariaDeAngelis I would agree but, as I said, I'm no Language-Lawyer. This conversation could easily become iterative, if not recursive. Further, if the first two examples are definitions, then I really can't see why yours is not.Thymol
@EnricoMariaDeAngelis: The standard does not use “inline” to mean “defined (or initialized) in a class”. It also doesn’t use code font to refer to properties (like inline or constexpr) that can be applied/implied other than by a keyword.Downdraft
R
0

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.

Ribaudo answered 17/5, 2020 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.