3

Given the example code:

struct S {
    char data[5];
    int a;
};

When running the "Run code analysis" in Microsoft Visual Studio, It warns to initialize all variables.

Now I know you can do this a number of ways, create a default constructor such as:

S() :
    data{0},
    a{0} {
}

That makes the warning go away. But what if you don't want to manually create the default constructor.

something like:

struct S {
    char data[5];
    int a = 0;
};

gets rid of the warning for a but not data, though you can fix that by adding {} after like so: char data[5]{}; this seems to make the code analysis happy.

That got me thinking, you can also initialize a like int a{0};

So my question is, are these all valid, and which is preferred?

Side note: I noticed std::array has _Ty _Elems[_Size]; which it never initializes anywhere, nor does it have {} after it, I'm assuming they just ignore this warning? Or are they doing something I'm not noticing to "fix" the warning?

Also wanted to add that this code: #include #include

template<class T, std::size_t N>
struct static_vector {
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N] = {0};

    T& operator[](std::size_t pos)  {
        return *std::launder(reinterpret_cast<T*>(&data[pos]));
    }
};

int main(int argc, char**) {
    static_vector<int, 10> s;
    s[0] = argc;
    return s[0];
}

under gcc9.1 -std=c++17 -Wall produces no warnings, yet the same code under clang8.0 -std=c++17 -Wall gives me:

warning: suggest braces around initialization of subobject [-Wmissing-braces]
        typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N] = {0};
                                                                              ^
                                                                              {}

I see that I can set it to = {}; which fixes it, just wondering why one compiler would produce a warning when the other doesn't? Which one is to spec?

Hex Crown
  • 753
  • 9
  • 22

2 Answers2

3

The guideline from CPPCoreGuidelines on this states: Don’t define a default constructor that only initializes data members; use in-class member initializers instead

So you can just do:

struct S {
    char data[5] = {0};
    int a = 0;
};

As to your other question about the lack of warning related to std::array, GCC has a note which states:

Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read.

I believe this would be true of MSVC as well.

P.W
  • 26,289
  • 6
  • 39
  • 76
  • So for c-arrays/std::arrays/std::vectors, etc, I should use `= {0};` as the "proper" way to initialize those data members? The reason I ask is because as I understand it, using = over just {} causes potential overhead? for instance, `std::string a("Hello World");` is preferred over `std::string = "Hello World"` as the 2nd one creates two instances of string, etc? – Hex Crown May 23 '19 at 09:07
  • @HexCrown: In case of primitive types, there is no difference. But if they are user-defined types, there are certain differences. BTW, the two kinds of initializations are called direct initialization and copy initialization. The differences are explained in this [post](https://stackoverflow.com/questions/1051379/is-there-a-difference-between-copy-initialization-and-direct-initialization). – P.W May 23 '19 at 09:14
  • I am a bit confused by the given reasoning (not included in your answer). How can the compiler generate a defaullt constructor that is more efficient than a self-written one that does nothing but initialize members? – 463035818_is_not_an_ai May 23 '19 at 09:38
  • I opened a [new question](https://stackoverflow.com/questions/56272431/how-can-a-compiler-generated-default-constructor-be-more-efficient-than-a-self-w) on that – 463035818_is_not_an_ai May 23 '19 at 09:44
  • @formerlyknownas_463035818: You must be referring to the text in the link. The text only says "The compiler-generated function ***can*** be more efficient". I think it is because the compiler is free to make certain optimizations for the constructor it generates that it may not be able to for a user-defined constructor. – P.W May 23 '19 at 09:45
  • @P.W yes I noticed that it says "can", but I fail to see what the compiler generated constructor could do better in the given example (or in any other example) – 463035818_is_not_an_ai May 23 '19 at 09:46
  • @HexCrown Prior to C++17, `std::string a = "Hello world"` is allowed as an optimization to create only one instance of string, and implementations do that quite well. Since C++17, this is [guaranteed copy elision](https://stackoverflow.com/questions/38043319/how-does-guaranteed-copy-elision-work). – L. F. May 23 '19 at 09:58
  • @P.W I was messing around in https://godbolt.org/ and under clang, doing `int data[5];` produces significantly less assembly than any form of that where data is initialized, ie `int data[5]{};`, `int data[5] = {0};`, etc. Yet under gcc the opposite is true, adding the initialization part seems to create less assembly than initializing it. Any idea whats going on there? – Hex Crown May 23 '19 at 15:30
  • @HexCrown: Compilers may have different approaches to optimization of code. You can ask a question about this issue if needed. There are some on SO who are very familiar with the inner workings of Clang and GCC and they might answer your question. – P.W May 24 '19 at 04:20
  • @HexCrown did you do that in debug mode on VS? debugging code there does a lot of extra things, e.g. external hooks to facilitate remote debugging of memory usage. – Swift - Friday Pie Oct 21 '21 at 18:36
3

In C++ for each declarator, the initializer may be one of the following:

1. ( expression-list )
2. = expression 
3. { initializer-list }

The description for these are as follows:

  1. comma-separated list of arbitrary expressions and braced-init-lists in parentheses
  2. the equals sign followed by an expression
  3. braced-init-list: possibly empty, comma-separated list of expressions and other braced-init-lists

Well which type of initialization to prefer actually depends upon context. To initialize data members in a class I personally prefer in class initialization using braced initializer, as in that case we don't have to write a user defined default constructor, compiler generated one is always efficient.

Class members

Non-static data members can be initialized with member initializer list or with a default member initializer.

In your case you can probably use:

struct S {
    char data[5] = {0}; //initialize by zero
    int a = 0;
};

or to give them different values also:

struct S {
    char data[5] = {0,1,2,3,4};
    int a = 0;
};

For more info see Initialization

Tejendra
  • 1,874
  • 1
  • 20
  • 32