C++11 Allows In-Class Initialization of Non-Static and Non-Const Members. What Changed

Why can't I initialize non-const static member or static array in class?

Why I can't initialize static data members in class?

The C++ standard allows only static constant integral or enumeration types to be initialized inside the class. This is the reason a is allowed to be initialized while others are not.

Reference:

C++03 9.4.2 Static data members

§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.

What are integral types?

C++03 3.9.1 Fundamental types

§7

Types bool, char, wchar_t, and the signed and unsigned integer types are collectively called integral types.43) A synonym for integral type is integer type.

Footnote:

43) Therefore, enumerations (7.2) are not integral; however, enumerations can be promoted to int, unsigned int, long, or unsigned long, as specified in 4.5.

Workaround:

You could use the enum trick to initialize an array inside your class definition.

class A 
{
static const int a = 3;
enum { arrsize = 2 };

static const int c[arrsize] = { 1, 2 };

};

Why does the Standard does not allow this?

Bjarne explains this aptly here:

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

Why are only static const integral types & enums allowed In-class Initialization?

The answer is hidden in Bjarne's quote read it closely,

"C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects."

Note that only static const integers can be treated as compile time constants. The compiler knows that the integer value will not change anytime and hence it can apply its own magic and apply optimizations, the compiler simply inlines such class members i.e, they are not stored in memory anymore, As the need of being stored in memory is removed, it gives such variables the exception to rule mentioned by Bjarne.

It is noteworthy to note here that even if static const integral values can have In-Class Initialization, taking address of such variables is not allowed. One can take the address of a static member if (and only if) it has an out-of-class definition.This further validates the reasoning above.

enums are allowed this because values of an enumerated type can be used where ints are expected.see citation above


How does this change in C++11?

C++11 relaxes the restriction to certain extent.

C++11 9.4.2 Static data members

§3

If a static data member is of const literal 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. 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 used in the program and the namespace scope definition shall not contain an initializer.

Also, C++11 will allow(§12.6.2.8) a non-static data member to be initialized where it is declared(in its class). This will mean much easy user semantics.

Note that these features have not yet been implemented in latest gcc 4.7, So you might still get compilation errors.

error: non-const static data member must be initialized out of line

Bjarne Stroustrup explains this here:

A class is typically declared in a header file and a header file is
typically included into many translation units. However, to avoid
complicated linker rules, C++ requires that every object has a unique
definition. That rule would be broken if C++ allowed in-class
definition of entities that needed to be stored in memory as objects.

As said by Stroustrup, every class needs a unique definition. Now, as we know static members are associated directly with their class. Now consider the two cases:

  1. The static member is also constant, then its initialization is allowed inline because the compiler can make its own optimisations and treat this member as a compile-time constant because it is guaranteed that its value will never change. So, as the value of this member is fixed, the definition of the class with which this member is associated is also fixed. So, the initialization is allowed inline.

  2. The static member is not constant. Then its value can change later on during the execution of the program. So, the compiler can not make compile-time optimisations on this member. Hence, to prevent the complications that may arise while trying to initialize such a member when the class is loaded, inline initialisation of such members is not allowed.

PS: When I heard about this concept the very first time, I was also confused because it is not in accordance with the principle of orthogonality that is a feature desired by programmers. The principle of orthogonality will state that since we can combine int and static; and int and const, we should be able to write static const int and static int in a similar fashion. But this case here is an example of a situation where the developer of a language has to give up orthogonality for the users of the language in exchange of the simplicity of the compilation process.

Have a look at the concept of orthogonality here

How to Ensure proper initialization of Non Static Data Members within a Class Template in C++

There are different way to do what you want depending upon which C++ version you're using. This is explained in more detail below:

C++11

template<typename T>
class MyVector
{
T x{};
};

Pre C++11

template<typename T>
class MyVector
{
T x;
MyVector(): x()
{
}
};

C++11

From C++11 and onwards, you can also write(using constructor initializer list):

template<typename T>
class MyVector
{
T x;
MyVector(): x{}
{
}
};

C++11

Note that this version won't work if the constructor for copy initialization is explicit since there is no mandatory copy elison.

#include <iostream>

using namespace std;
struct Name
{
explicit Name(const Name&)
{

}
Name() = default;
};
template<typename T>
class MyVector
{
public:
T x = T();

};
int main()
{
cout<<"Hello World";
MyVector<int> p; // works with C++11,C++17 etc
MyVector<Name> n; //error with C++11 and C++14
return 0;
}

But the above version will work with C++17 since there is mandatory copy elison in C++17.

#include <iostream>

using namespace std;
struct Name
{
explicit Name(const Name&)
{

}
Name() = default;
};
template<typename T>
class MyVector
{
public:
T x = T();

};
int main()
{
cout<<"Hello World";
MyVector<int> p; // works with C++11,C++17 etc
MyVector<Name> n; //works with C++17 due to mandatory copy elison
return 0;
}

uninit_member: Non-static class member m_wszParams is not initialized in this constructor nor in any functions that it calls in C++

At a minimum


class IVerification
{
public:
IVerification() = default;
virtual ~IVerification() = default;

protected:
wchar_t* m_wszParams = nullptr;
wchar_t* m_wszParamType = nullptr;
};

Next step: pointers are not your friends, use std::string.

error: ISO C++ forbids in-class initialization of non-const static member

The initialization of the static member counter must not be in the header file.

Change the line in the header file to

static int counter;

And add the following line to your employee.cpp:

int Employee::counter = 0;

Reason is that putting such an initialization in the header file would duplicate the initialization code in every place where the header is included.

Why can one initialize non-const and static const member variables but not static member variables?

It has to do with where the data is stored. Here's a breakdown:

  • int: member variable, stored wherever the class instance is stored
  • const int: same as int
  • static const int: doesn't need to be stored, it can simply be "inlined" where used
  • static int: this must have a single storage location in the program...where?

Since the static int is mutable, it must be stored in an actual location somewhere, so that one part of the program can modify it and another part can see that modification. But it can't be stored in a class instance, so it must be more like a global variable. So why not just make it a global variable? Well, class declarations are usually in header files, and a header file may be #included in multiple translation units (.cpp files). So effectively the header file says "there is an int...somewhere." But the storage needs to be put into the corresponding .cpp file (like a global variable).

In the end, this is not really about initialization, but rather the storage. You could leave off the initializer and you'd still not have a valid program until you add this to your .cpp file:

int A::d; // initialize if you want to, default is zero

Without this, references to the static int will be undefined and linking will fail.



Related Topics



Leave a reply



Submit