Defining Static Const Integer Members in Class Definition

Defining static const integer members in class definition

My understanding is that C++ allows static const members to be defined inside a class so long as it's an integer type.

You are sort of correct. You are allowed to initialize static const integrals in the class declaration but that is not a definition.

Interestingly, if I comment out the call to std::min, the code compiles and links just fine (even though test::N is also referenced on the previous line).

Any idea as to what's going on?

std::min takes its parameters by const reference. If it took them by value you'd not have this problem but since you need a reference you also need a definition.

Here's chapter/verse:

9.4.2/4 - If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

See Chu's answer for a possible workaround.

Can I initialize static const class member?

The standard explicitly states that non-inline static data members of a class are declaration only, and must be followed up by definition at namespace scope: class.static.data#3

3:
The declaration of a non-inline static data member in its class definition is not a definition and may be of an incomplete type other than cv void. The definition for a static data member that is not defined inline in the class definition shall appear in a namespace scope enclosing the member's class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the ​::​operator. The initializer expression in the definition of a static data member is in the scope of its class ([basic.scope.class]).

These rules are then refined further so that non-volatile non-inline const static data member of integral or enumeration type can be brace initialised (in effect defining the variable at the point of declaration): class.static.data#4

4: If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]). The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer. An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer.

In non-standardese speak, this says that you must define static member variables separately from the definition except for the case of integers or enums, which explains why your example fails.

Defining static const variable in C++

You need to provide a definition for ClassA::SIZE, but still give the constant integral value at the point of declaration:

/* ClassA.h */
class ClassA{
public:
static const size_t SIZE = 10; // value here
int array[SIZE];
funcA();
funcB();
...
};


/* ClassA.cpp */
const size_t ClassA::SIZE; // no value here

C++ static const class members initialization

... without the need to declare it somewhere in the scope. Is that by any chance possible?

No, it's not possible without declaring it (which you already tried to do in your class declaration). You probably meant, without defining it outside your class declaration. Again the answer is no.

You have to separate declaration and definition for this case (it only works with primitive integral types like int to initialize these directly in the class declaration).

First have a simple declaration in your class declaration (usually something like CData.hpp)

namespace misc {
class CData {
public:
CData( ) { };
CData( int d );

CData& operator = ( const CData& d );

static const CData& FIRST;

private:
int data;
};
}

and then define it in a separate compilation unit (usually something like CData.cpp)

namespace misc {
const CData& CData::FIRST = CData( 512 );
}

const static member initialization - inside vs outside class definition

What are the differences between the two initializations above? Why does one cause the error with stl containers?

The argument type of std::vector<int>::push_back() is int const&. Whenever a variable is used by reference or pointer, it must be defined.

A simple change to Class::f implementation will obviate the need to define Class::var.

void Class::f() {
std::vector<int> vec;
int v = var;
vec.push_back(v);
}

Here, var is not used by reference. Hence, there is no need to define Class::var.

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.

C++ standard for defining public variable static const integer

Here is a full quote from the C++ Standard about constant static members:

If a non-volatile const static data member is of integral or
enumeration type, its declaration in the class definition can specify
a brace-or-equal-initializer in which every initializer-clause that is
an assignmentexpression is a constant expression (5.19). A static data
member of literal type can be declared in the class definition with
the constexpr specifier; if so, its declaration shall specify a
brace-or-equal-initializer in which every initializer-clause that is
an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. —end note
] The member shall still be defined in a namespace scope if it is
odr-used (3.2) in the program and the namespace scope definition shall
not contain an initializer
.

So if a const static member is not ODR-used then its definition outside the class is not required.

Note: by the way in this context it is unimportant whether a const static member is public or not.

How to define static const member?

Say:

Test::Dummy const Test::dummy = { };


Related Topics



Leave a reply



Submit