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.