Reputation: 571
In the example below, Y and X give a warning "variable has static storage duration and non-POD type" (pclint, Autosar A3-3-2).
struct Y {int t; Y() {t = 0;}};
class X {private: int t; public: X() {t = 0;}};
struct Z {int t;};
X x; // warning: variable 'x' has 'static' storage duration and non-POD type
Y y; // variable 'y' has 'static' storage duration and non-POD type
Z z;
I have 2 questions.
Edit: In my case the global variable is only used in the standard namespace and data is accessed by global functions in this namespace. Therefore the constructor should be executed before data is accessed.
One solution could be the use of a C++ wrapper class which would initialise the struct. Is there a simpler/alternative solution, where uninitialised use of member "int t" cannot occur?
Upvotes: 2
Views: 699
Reputation: 73186
From the AutoSAR C++14 Guidelines:
Rule A3-3-2 (required, implementation, automated)
Static and thread-local objects shall be constant-initialized.
Rationale
In general, using non-const global and static variables obscures the true dependencies of an API, since they can be accessed from any place of the source code. It therefore makes the code more difficult to maintain, less readable, and significantly less testable.
A particular problem is that the order in which constructors and initializers for static variables are called is only partially specified by the C++ Language Standard and can even change from build to build. This can cause issues that are difficult to find or debug.
[...]
What could happen before the constructor is called which justifies the warning?
The general rationale for the rule is to avoid the complex rules of where dynamic initialization applies for static initialization, a common cause for e.g. the static initialization fiasco.
How can I avoid the warning which occurs for Y and X? I want to avoid possible uninitialised state if used with automatic storage (as for Z), therefore I would like to keep the constructor or reach the goal somehow else.
A simply fix for your examples is to make their constructors constexpr
, such that the static
storage duration objects x
and y
are constant-initialized instead of zero-initialized as part of static initialization:
Constant initialization is performed instead of zero initialization of the static and thread-local (since C++11) objects and before all other initialization. Only the following variables are constant initialized:
[...]
- Static or thread-local object of class type that is initialized by a constructor call, if the constructor is constexpr and all constructor arguments (including implicit conversions) are constant expressions, and if the initializers in the constructor's initializer list and the brace-or-equal initializers of the class members only contain constant expressions.
E.g. (moving the initialization of the t
data member by a constant expression to its default member initializer):
struct Y {
int t{0};
constexpr Y() {}
};
class X {
private:
int t{0};
public:
constexpr X() {}
};
X x; // static initialization is constant-initialization
Y y; // static initialization is constant-initialization
or alternatively initializing the t
member in the member initializer list of the constexpr
constructor:
struct Y {
int t;
constexpr Y() : t{0} {}
};
class X {
private:
int t;
public:
constexpr X() : t{0} {}
};
Finally, note that POD is no longer a standardese term as of C++11 (deprecated), and as the AutoSAR C++14 Guidelines naturally applies for C++14, one should consider entirely dropping the notion of POD class types; this likewise applies for pclint.
Upvotes: 1