26

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.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 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. – Werner Henze May 17 '20 at 18:40
  • 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?_ – Enlico May 17 '20 at 19:04
  • C++17 introduces `inline static`, and it's awesome. – Matthieu M. May 18 '20 at 07:42
  • [recent, related](https://stackoverflow.com/q/61781901/1132334) – Cee McSharpface May 18 '20 at 10:33
  • In hindsight, [this](https://stackoverflow.com/questions/18749071/why-does-a-static-data-member-need-to-be-defined-outside-of-the-class) is also related. – Enlico Dec 23 '20 at 05:56

3 Answers3

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.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • The critical phrase here is [initializing declaration](http://eel.is/c++draft/dcl.init#def:initializing_declaration). – Davis Herring May 17 '20 at 19:17
  • 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? – Enlico May 18 '20 at 17:38
  • 1
    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. – Piotr Skotnicki May 18 '20 at 22:33
  • 1
    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*. – Piotr Skotnicki May 19 '20 at 05:45
  • 1
    And the standard makes it explicitly a non-definition in [\[basic.def\]/p2.3](https://eel.is/c++draft/basic.def#2.3), unless you put also the `inline` keyword. – Piotr Skotnicki May 19 '20 at 10:33
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!

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • But the /2.3 condition *does* apply here! – Davis Herring May 17 '20 at 19:13
  • @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. – Adrian Mole May 17 '20 at 19:15
  • @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. – Enlico May 17 '20 at 19:18
  • @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. – Adrian Mole May 17 '20 at 19:20
  • @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. – Davis Herring May 17 '20 at 19:50
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.

Dragan
  • 66
  • 5