GCC wrong `missing initializer for member <anonymous>` warning? - c++

I'm trying to run the following code in my project:
#include <memory>
enum class Type
{
SpecificType
};
struct BaseData
{
using Ptr = std::unique_ptr<BaseData>;
BaseData(Type type) noexcept : type(type) {}
const Type type;
};
template <typename T, Type VType>
struct Data : BaseData
{
using Ptr = std::unique_ptr<T>;
Data() noexcept : BaseData(VType) {}
static Ptr make(T&& val) { return std::make_unique<T>(std::move(val)); }
};
struct SpecificData : Data<SpecificData, Type::SpecificType>
{
int field1;
int field2;
};
int main()
{
SpecificData::Ptr data = SpecificData::make({ .field1 = 13, .field2 = 42 });
return 0;
}
My issue is the warning which I get from gcc (trunk)
<source>:35:48: error: missing initializer for member 'SpecificData::<anonymous>' [-Werror=missing-field-initializers]
35 | SpecificData::Ptr data = SpecificData::make({ .field1 = 13, .field2 = 42 });
The compiler options used for compilation are:
-std=c++20 -Wall -Wextra -Wpedantic -Werror -O2
I know how to deactivate the warning, or in this case the error beacause of the -Werror, but I want to understand it first, why is it present in the first place.
I don't understand which field remains uninitialized in my SpecificData object.
Could it be the const Type type from the base class which causes the problem?
If not, is this a compiler bug?
Note : clang compiles the code without issue with the same flags, so does MSVC with similar flags.

There is nothing wrong with the code you are showing, but the warning may be intentional.
An aggregate class has several elements, in order its direct base classes, followed by its direct non-static data members.
When you use aggregate initialization (whether with designated initializers or not) you can either explicitly initialize any of these elements by writing an element corresponding to it in the initializer list, or have them be implicitly initialized.
Obviously there shouldn't be a warning for the explicitly initialized ones, but the question is whether every non-explicitly initialized element should produce the warning.
In general, it should be necessary to provide an initializer for every element, including the direct base class subobjects.
However, since C++11 it is possible to declare in-class default member initializers for non-static data members, which would be chosen in this scenario. This clearly signifies the intent to not need explicit initialization, so there is no warning for these.
Unfortunately there doesn't exist an analoguous syntax for base classes and so it is not possible to signify a proper "default" in aggregate initialization for them.
All remaining elements (non-static data members without default member initializer and direct base classes), are simply initialized as if by = {}. In your particular case the base class Data<SpecificData, Type::SpecificType> can be initialized just fine that way without leaving any subobject with indeterminate value, because it has a default constructor that will be chosen for the initialization and initialize the only member subobject (type) correctly.
But it isn't clear from the definition of SpecificData that this is permitted use. The warning isn't meant to warn about indeterminate values in non-initialized data members anyway, since aggregate initialization will never leave elements uninitialized. It is meant to warn that something has taken a default value through = {} initialization, that is likely a mistake.
To see that this is GCC's behavior (which I unfortunately couldn't find clearly defined in the documentation), consider the following two cases that also produce the warning:
struct Data
{
};
struct SpecificData : Data
{
int field1;
int field2;
};
int main()
{
// Data without explicit initializer
SpecificData d{ .field1 = 13, .field2 = 42 };
}
And
struct Data
{
};
struct Data2
{
};
struct SpecificData : Data, Data2
{
int field1 = 1;
int field2 = 2;
};
int main()
{
SpecificData d{{}}; // no explicit initializer for Data2
}

Related

uninit_member: Non-static class member field m_cJobState.bstatus is not initialized in this constructor nor in any functions that it calls

I am getting the below warning at SubManager constructor:
Uninitialized scalar field (UNINIT_CTOR)
uninit_member: Non-static class member field m_cJobState.bEULA is not initialized in this constructor nor in any functions that it calls.
uninit_member: Non-static class member field m_cJobState.bstatus is not initialized in this constructor nor in any functions that it calls.
uninit_member: Non-static class member field m_cJobState.eActivationState is not initialized in this constructor nor in any functions that it calls.
Below is the code snippet:
SubManager.h file
struct MgrStatus
{
bool bEULA;
bool bstatus;
WorkState eActivationState;
};
class SubManager
{
private:
MgrStatus m_cJobState;
};
SubManager.cpp file
SubManager::SubManager()
{
}
To resolve the warning,
Do we need to initialize structure variables in the above constructor?
If we need to initialize, how to initialize the enum variable (WorkState eActivationState;)?
You did not specify from what tool you got warnings and also posted code
should give different errors I suspect. Seems that the tool requires you to initialize all aggregate members in constructors (C++ standard does not).
Since C++11 (question was tagged like that) it should compile and run with uniform initialization of aggregate members:
enum WorkState {SomethingElse, Crappy, Etc}; // wasn't defined in OP
struct MgrStatus
{
bool bEULA;
bool bstatus;
WorkState eActivationState;
};
class SubManager
{
public:
SubManager(); // constructor declaration was missing in OP
private:
MgrStatus m_cJobState;
};
SubManager::SubManager()
: m_cJobState{false,false,Crappy}
{ }
int main() // to demo it runs
{
SubManager s;
(void)s; // just to silence unused variable s warning
}

What's the differences between member initializer list and default member initializer on non-static data member?

I'd like to understand what's the differences of using one form rather than the other (if any).
Code 1 (init directly on variables):
#include <iostream>
using namespace std;
class Test
{
public:
Test() {
cout<< count;
}
~Test();
private:
int count=10;
};
int main()
{
Test* test = new Test();
}
Code 2 (init with initialization list on constructor):
#include <iostream>
using namespace std;
class Test
{
public:
Test() : count(10) {
cout<< count;
}
~Test();
private:
int count;
};
int main()
{
Test* test = new Test();
}
Is there any difference in the semantics, or it is just syntactic?
Member initialization
In both cases we are talking about member initialization.
Keep in mind that the members are initialized in the sequence in which they are declared in the class.
Code 2: Member initializer list
In the second version:
Test() : count(10) {
: count(10) is a constructor initializer (ctor-initializer) and count(10) is a member initializer as part of the member initializer list. I like to think of this as the 'real' or primary way that the initialization happens, but it does not determine the sequence of initialization.
Code 1: Default member initializer
In the first version:
private:
int count=10;
count has a default member intitializer. It is the fallback option. It will be used as a member initializer if none is present in the constructor, but in the class the sequence of members for initialization is determined.
From section 12.6.2 Initializing bases and members, item 10 of the standard:
If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given
struct A {
int i = / some integer expression with side effects / ;
A(int arg) : i(arg) { }
// ...
};
the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or-equalinitializer will not take
place. —end example ]
Something else to keep in mind would be that if you introduce a non-static data member initializer then a struct will no longer be considered an aggregate in C++11, but this has been updated for C++14.
Differences
what's the differences of using one form rather than the other (if
any).
The difference is the priority given to the two options. A constructor initializer, directly specified, has precedence. In both cases we end up with a member initializer via different paths.
It is best to use the default member initializer because
then the compiler can use that information to generate the constructor's initializer list for you and it might be able to optimize.
You can see all the defaults in one place and in sequence.
It reduces duplication. You could then only put the exceptions in the manually specified member initializer list.
In the C++ Core Guidelines (see note 1 below), Guideline C.48 recommends the first approach (in-class initializers.) The reasoning provided is:
Makes it explicit that the same value is expected to be used in all constructors. Avoids repetition. Avoids maintenance problems. It leads to the shortest and most efficient code.
In fact if your constructor does nothing but initialize member variables, as in your question, then Guideline C.45 is firmer still, saying to use in-class initializers for sure. It explains that
Using in-class member initializers lets the compiler generate the function for you. The compiler-generated function can be more efficient.
I am not going to argue with Stroustrup, Sutter, and several hundred of their friends and colleagues even if I haven't written a compiler so I can't prove it's more efficient. Use in-class initializers wherever you can.
If you're not familiar with the guidelines do follow the links to see sample code and more explanations.
The difference I can think of is that member initializer list is prior to default member initializer.
Through a default member initializer, which is simply a brace or
equals initializer included in the member declaration, which is used
if the member is omitted in the member initializer list.
If a member has a default member initializer and also appears in the
member initialization list in a constructor, the default member
initializer is ignored.
For example:
class Test
{
public:
Test() {} // count will be 10 since it's omitted in the member initializer list
Test(int c) : count(c) {} // count's value will be c, the default member initializer is ignored.
private:
int count = 10;
};
There is no difference in the code. The difference would come if you would be would have more than one constructor overload and in more than one count would be 10. With the first version you would have less writing to do.
class Test
{
public:
Test() = default;
Test(int b) : b(b) {} // a = 1, c = 3
~Test();
private:
int a = 1;
int b = 2;
int c = 3;
};
As opposed to the second version where the above code would look like this:
class Test
{
public:
Test() : a(1), b(2), c(3) {}
Test(int b) : a(1), b(b), c(3) {}
~Test();
private:
int a;
int b;
int c;
};
The difference gets bigger with more member variables.
When you initialise next to the declaration of the member, this is valid only in C++11 onwards, so if you're in C++98/03 you outright cannot do this.
If the value never changes, you could choose to make this a constexpr static instead and the compiler would then be required to not use any extra storage for the value (so long as you don't define it) and instant use constant propagation wherever the value is used instead.
One disadvantage of using the by-declaration syntax is that it must be in the header, which will result in a recompile of all translation units that include the header every time you want to change its value. If this takes a long time, that might be unacceptable.
Another difference is that using the member initialisation list lets you change the value for each constructor, whilst using the by-declaration version only allows you to specify one value for all constructors (although you could overwrite this value ... but I'd personally avoid this as it could get quite confusing!).
As an aside, there's no need to use new here to create an instance of Test. This is a common mistake when people come to the language from other languages and I wanted to make you aware. There are of course many uses for doing this outside of your example.

How to prevent default initialization of a const variable with a class type

I have a custom class that I want to behave like a built-in type.
However I have noticed that you can initialise a const variable of that class without providing an initial value. My class currently has an empty default constructor.
Here is a comparison of int and my class foo:
int a; // Valid
int a = 1; // Valid
const int a = 1; // Valid
const int a; // Error
foo a; // Valid
foo a = 1; // Valid
const foo a = 1; // Valid
const foo a; // Should cause an error, but it compiles
As you can see I need to prevent
const foo a;
from compiling.
Any ideas from C++ gurus?
It compiles only if it has a default constructor, and it compiles because it has it, which means that it is initialized. If you don't want that line to compile, just disable the default constructor (will also make foo a; an error as an unwanted side effect). Without a definition of foo or what you want to do, this is as far as I can get.
I don't think there is any way of achieving what you want (i.e. allow the non-const variable to be default initialized, while having the const version fail compilation and allowing the other use cases --that require providing constructors)
The rules of C++ simply say that default-initialization (e.g. new T;) and value-initialization (e.g. new T();) are the same for objects of class type, but not for objects of fundamental type.
There's nothing you can do to "override" this distinction. It's a fundamental part of the grammar. If your class is value-initializable, then it is also default-initializable.
There is a sort-of exception for classes without any user-defined constructors: In that case, initialization of members is done recursively (so if you default-init the object, it tries to default-init all members), and this will fail if any of the class members are themselves fundamental, or again of this nature.
For example, consider the following two classes:
struct Foo { int a; int b; };
struct Goo { int a; int b; Goo(){} };
//const Foo x; // error
const Goo y; // OK
The implicit constructor for Foo is rejected because it doesn't initialize the fundamental members. However, y is happily default-initialized, and y.a and y.b are now "intentionally left blank".
But unless your class doesn't have any user-defined constructors, this information won't help you. You cannot "forward" the initialization type to a member (like Foo() : INIT_SAME_AS_SELF(a), b() { }).

typedef struct : Default Initialization

typedef struct foo
{
bool my_bool;
int my_int;
} foo;
In the example above I understand that my_bool will be initialized randomly to either true or false but what about my_int? I assumed that my_int would be default initialized to 0 but that seems not to be the case.
Defining structs in this way appears to be incompatible with initialization lists so what is the best way to initialize my_bool and my_int to false and 0 respectively?
Types don't get "initialized". Only objects of some type get initialized. How and when they get initialized depends on how and where the corresponding object is defined. You provided no definition of any object in your question, so your question by itself doesn't really make much sense - it lacks necessary context.
For example, if you define a static object of type foo
static foo foo_object; // zeros
it will be automatically zero-initialized because all objects with static duration are always automatically zero-initialized.
If you define an automatic object of type foo without an initializer, it will remain uninitialized
void func()
{
foo foo_object; // garbage
}
If you define an automatic object of type foo with an aggregate initializer, it will be initialized in accordance with that initializer
void func()
{
foo foo_object1 = { 1, 2 }; // initialized
foo foo_object2 = {}; // initialized with zeros
}
If you allocate your object with new and provide no initializer, it will remain uninitialized
foo *p = new foo; // garbage in `*p`
But if you use the () initializer, it will be zero-initialzed
foo *p = new foo(); // zeros in `*p`
If you create a temporary object of type foo using the foo() expression, the result of that expression will be zero-initialized
bool b = foo().my_bool; // zero
int i = foo().my_int; // zero
So, once again, in your specific case the initialization details depend on now you create the object of your type, not on your type itself. Your type itself has no inherent initialization facilities and doesn't interfere with the initialization in any way.
Implement a default constructor:
typedef struct foo
{
foo()
: my_bool(false), my_int(0)
{
// Do nothing
}
bool my_bool;
int my_int;
} foo;
First off, the way that struct is declared is in the style of C. In C++ you should just do:
struct foo
{
bool my_bool;
int my_int;
};
In both C and C++, initialization is a separate step from allocation. If you always want to initialize the members of your struct, use default initialization syntax like this:
struct foo
{
bool my_bool{};
bool my_int{};
};
In older versions of C++ you need to manually write a default constructor that initializes all the members (the newer syntax above is just sugar for this):
struct foo
{
foo() : my_bool(), my_int() { }
bool my_bool;
int my_int;
};
As #sbi notes, if you want to manually initialize the struct, even without the default constructor, you can do foo myFoo = foo();
Have a default constructor:
struct foo {
foo() : my_bool(false), my_int(0) {}
bool my_bool;
int my_int;
};
You are not creating any object in that code. Initialization is done when you create objects, and is not particularly tucked by the way you declare the struct.
For instance the following initializes the boolean to false and the integer to 0
foo f = { };
Notice that you have just typdefed your struct. You have not created an object. Like others said you can omit the typedef in C++ and just declare the struct, and you are still able to refer to the type by just saying foo.
If you omit explicit initialization when you define an object, then for sure no initialization is done unless the object is defined at namespace scope or defined as static locally (in which case all members are zero-initialized) or the class has a user defined default constructor that does initialization accordingly.
As long as you are declaring the structs in a C-way, you could use zeromemory to null exactly sizeof(foo) bytes, therefore defaulting all values to 0.
In C++, you could define your structure with a constructor which would set your values to some default values if needed.
c and c++ don't initialize variables at all. They contain whatever happened to be in the memory location that they're now in previously. This also applies for member variables in classes and structs unless you specifically initialize them to a value.

What if a constructor parameter has the same name as a member variable in C++?

Some code first:
class CInner {
public:
CInner( const CInner& another ) { //impl here }
private:
// some member variables
}
class COuter {
public:
COuter( const CInner& inner ) : inner( inner ) {}
private:
CInner inner;
}
Yes, in COuter::COuter( const CInner& ) the parameter has the same name as the member variable.
In VC++ that works - VC++ gets the idea that it is only reasonable to initialize the member variable with the parameter and that's what happens - CInner::inner gets initialized with the parameter. But when the same is compiled with GCC it is interpreted in another way: GCC initializes CInner::inner with itself and so it is left uninitialized.
Which of the compilers is right?
It is not really about some specific compiler deciding what's reasonable and what's not. The language specification explicitly says that in inner(inner) used in the constructors initializer list the first inner should be looked up in class scope (i.e. resolve to COuter::inner), while the second innershould be looked up in the constructor scope (i.e. resolve to constructor parameter inner).
This is what you described as VC++ behavior. However, I find it hard to believe that GCC would behave incorrectly in this case (unless you have some weird old version of GCC). Are you sure you haven't misinterpreted GCC's behavior somehow?
Visual C++ is correct. I suspect you're using an older version of gcc for your test -- at least as I recall, recent ones do this correctly. This is covered in §12.6.2/7 of the standard, which gives the following example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
initializes X::r to refer to X::a, initializes X::b with the value of the constructor
parameter i, initializes X::i with the value of the constructor parameter i, [ ...]