In C++, static members may not be initialized in the class body with these exceptions:
static members of const integral type can be
static members of constexpr literal type must be
Can you explain why these exceptions?
Also, this holds:
Even if a const static data member is initialized in the class body, that member ordinarily should be defined outside the class definition.
This I never understood at all. What's the point of this extra definition?
Just trying to get some intuitions here.
Why can there be an initializer in the class definition?
Concerning the two exceptions for const and constexpr static data members:
[class.static.data]/3
[ Note: In both these cases, the member may appear in constant expressions. — end note ]
I.e. with an initializer, you may use them in constant expressions, e.g.
struct s
{
static std::size_t const len = 10;
int arr[len];
};
std::size_t const s::len;
If len wasn't initialized in the class definition, the compiler couldn't easily know its value in the next line to define the length of arr.
One could argue about allowing initializers for of non-const, non-constexpr static data members in the class definition, but this could interfere with the initialization order:
[basic.start.init]/2
Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization. Other non-local variables with static storage duration have ordered initialization.
That is, the order of the definitions including initializers is important. The order of (dynamic) initialization of non-local objects is only defined within a translation unit, this is another reason why there has to be a definition including initializer for non-const, non-constexpr static data members.
What's the point of this extra definition?
This has already been answered in the comments IMO. You might want to add the ODR, that is, as a name with external linkage, the static data member must (only) be defined in one translation unit (if it's ODR-used). It's up to the programmer to choose this translation unit.
Related
What is the right way to initialize static data members in C++? I'm also interested in how it has changed from C++98, to C++11 to C++14.
Here is an example:
// bufferedOutput.h
class BufferedOutput
{
// Static member declaration.
static long bytecount;
};
// bufferedOutput.cpp
long BufferedOutput::bytecount = 50;
Are there other ways to initialize static data members?
The rules have always been as follows:
A const static data member (SDM) of integral or enumeration type can be initialised in class with a constant expression.
A constexpr SDM must be initialised in class with a constant expression.
C++17 no longer requires an initializer when the default constructor initialises every member. Also, constexpr SDMs are implicitly inline variables, which makes their declaration a definition (external definitions are now deprecated).
Other kinds of SDMs can have an initializer at their definition (which is in class if that SDM is declared inline).
Nothing has substantially changed between C++03 and C++11+ for code that is valid in both languages.
Note that for SDMs that are not inline, the in-class declaration is not a definition—regardless of whether an initializer is provided—and they must be defined if they are odr-used.
As of C++17, we can make your SDM inline, which makes its in-class declaration a definition:
class BufferedOutput
{
static inline long bytecount = 50;
};
I would like to initialize my private static member "pi" in the definition of the class in order to tidy my code better having the initializations in the .cpp files.
When I try this error shows up: "Declaration of constexpr static data member 'pi' requires an initializer"
I'm using CLion 2018.3.4 and C++ 11.
I tried to work around and the only solution is to initialize the member in the declaration.
Other answers on Stack Overflow provided me a better knowledge but didn't answer my question.
// .h file
class Shape {
public:
virtual void getArea();
private:
static constexpr float pi; // the error shows up here
};
// .cpp file
#include "Shape.h"
const float Shape::pi = 3.14; //here I don't exactly know why it does not require constexpr, but it's fine even with and without const
// what I think this is equivalent to (in .h file)
static constexpr float pi = 3.14;
I was expecting this to work like I was assigning "3.14" in the declaration.
I'm not defining a constructor, in that case I know it wouldn't work because the c'tor is meant to initialize an instance of the class while a static member is supposed to be already initialized being a global element in the "Shape" namespace.
What I suppose it is happening is that the linker tries to initialize the member in the header because it has to be done in the pre-processing phase while the .cpp file is used later.
With static constexpr members you cannot leave off the initializer in the class definition. A constexpr variable must be initialized when declared because it can be used after it is declared in a constant expression. This is detailed in [class.static.data]/3 of the C++11 standard
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 assignment-expression is a constant expression ([expr.const]). 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 ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer.
emphasis mine
So, with that, your code needs to be
// .h file
class Shape {
public:
virtual void getArea();
private:
static constexpr float pi = 3.14; // we initialize here so it can be used.
};
// .cpp file
constexpr float Shape::pi; // we define here so it can be odr-used
Do note that this has changed in C++17. With the introduction of inline variables static constexpr member variables no longer need to be defined outside of the class. The compiler will handle it for you and ensure only a single defentition of the object exists. You can still define the member if you want, but that ability is deprecated and will most likely be removed in a future standard revision. The new text for [class.static.data]/3 is
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. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see [depr.static.constexpr]). Declarations of other static data members shall not specify a brace-or-equal-initializer.
emphasis mine
and [dcl.constexpr]/1 says that a static constexpr variable is implicitly inline
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template. The consteval specifier shall be applied only to the declaration of a function or function template. A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable ([dcl.inline]). If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.
emphasis mine
When I tried to initialize the static member variables at the time of declaration inside the class, compiler is throwing error as expected because we need to explicitly allocate the space for static member variables outside the class. I thought this should be same for static const variables. But to my surprise, initialization of static const member variables inside the class is working fine. Can any one please let me know why normal static member variable initialization is not allowed in the same way?
I assume that you meant
// inside class definition block
static const int a = 0;
static int b = 0; // error
C++ Standard 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 name- space scope if it is used in the program and the namespace scope definition shall not contain an initializer.
It is specified in standard.
Edit:
as M.M pointed out, above quotation is actually not what the Standard says, and the correct one is C++ Standard 12.2.3.2/3
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 (8.20). The member shall still be defined in a namespace scope
if it is odr-used (6.2) 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 .
If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no
initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a
brace-or-equal-initializer.
One needs a bit of space in memory. Const do not - they can be hard coded.
classes are usually declared in header files. Header files can be included multiple times in body files. If a static member, which needs memory, is defined within a class, than there will be a different copy of such a member in different body files. This will kill the idea of static members.
Constants on the other hand do not use memory and are compile-only constructs. therefore declaring them in the classes does not do any harm.
In the c++ standard it is specified that within the class member-specification (class body), the class can be considered completely-defined, but not for static data member initializer [class.mem]:
A class is considered a completely-defined object type (6.9) (or complete type) at the closing } of the
class-specifier. Within the class member-specification, the class is regarded as complete within function
bodies, default arguments, noexcept-specifiers, and default member initializers (including such things in
nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
EDIT: This is a citation from N4687, wording has changed but I do not believe the meaning changed.
I was expecting such code to compile:
struct enum_like
{
static constexpr enum_like enum_member{};
};
Why such a definition disallowed by the C++ standard?
I believe compilers could proceed this way:
read member declaration, not definition until class definition closing brace. (Now the compiler has a completely defined class)
Analyse static data-member initializer (This way compilers have the constant definition of constexpr members)
Analyse other member definitions.
And then resolve recursions for static member intializer as is specified in [decl.init] for non static members!
This rule forbids problematic things like:
struct A {
static constexpr std::size_t N = sizeof(A);
char buffer[N+2];
};
What is the right way to initialize static data members in C++? I'm also interested in how it has changed from C++98, to C++11 to C++14.
Here is an example:
// bufferedOutput.h
class BufferedOutput
{
// Static member declaration.
static long bytecount;
};
// bufferedOutput.cpp
long BufferedOutput::bytecount = 50;
Are there other ways to initialize static data members?
The rules have always been as follows:
A const static data member (SDM) of integral or enumeration type can be initialised in class with a constant expression.
A constexpr SDM must be initialised in class with a constant expression.
C++17 no longer requires an initializer when the default constructor initialises every member. Also, constexpr SDMs are implicitly inline variables, which makes their declaration a definition (external definitions are now deprecated).
Other kinds of SDMs can have an initializer at their definition (which is in class if that SDM is declared inline).
Nothing has substantially changed between C++03 and C++11+ for code that is valid in both languages.
Note that for SDMs that are not inline, the in-class declaration is not a definition—regardless of whether an initializer is provided—and they must be defined if they are odr-used.
As of C++17, we can make your SDM inline, which makes its in-class declaration a definition:
class BufferedOutput
{
static inline long bytecount = 50;
};