C++ proper way to inline initialize member variables - c++

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?

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.

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:
comma-separated list of arbitrary expressions and braced-init-lists in parentheses
the equals sign followed by an expression
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

Related

How to obtain constexpr `.size()` of a non-static std::array member

Given that std::array<T,N>::size is constexpr, in the snippet below
Why does it matter that Foo1::u is not a static member? The type is known at compile time and so is its size().
What's wrong with Foo2::bigger()?
Listing:
// x86-64 gcc 10.1
// -O3 --std=c++20 -pedantic -Wall -Werror
#include <array>
#include <cstdint>
union MyUnion {
std::array<uint8_t,32> bytes;
std::array<uint32_t,8> words;
};
struct Foo1 {
MyUnion u;
static constexpr size_t length {u.bytes.size()};
//invalid use of non-static data member 'Foo1::u'
};
struct Foo2 {
MyUnion u;
size_t x;
consteval int8_t length() const { return u.bytes.size(); };
bool bigger() const { return x > length(); }
//'this' is not a constant expression
};
I would like to keep std::array length in MyUnion declaration and not resort to
constexpr size_t LEN {32};
union MyUnion {
std::array<uint8_t,LEN> bytes;
std::array<uint32_t,LEN/4> words;
};
These situations are a bit different.
In the first one:
static constexpr size_t length {u.bytes.size()};
//invalid use of non-static data member 'Foo1::u'
You're trying to access a non-static data member without an object. That's just not a thing you can do. There needs to be some Foo1 associated with this expression. In the context of a non-static member function, it'd implicitly be this->u, but there still needs to be some object there. There's only one exception: you're allowed to write sizeof(Foo1::u).
In the second one:
consteval int8_t length() const { return u.bytes.size(); };
Here, the this pointer is not itself a constant expression (we don't know what it points to), so accessing anything through it fails. This is part of a broader case of constant expressions using unknown references in a way that doesn't really affect the constant-ness of the expression (see my blog post on the constexpr array size problem). I recently wrote a paper on this topic which was focused primarily on references, but this is a narrow extension on top of that.
Either way, for now this can't work either, because everything has to be a constant. So you'll have to resort to something along the lines of what you suggest: expose the constant you want separately.
I recommend "resorting to" a variable to define the size in the first place:
union MyUnion {
static constexpr std::size_t size = 32;
using byte = std::uint8_t;
using word = std::uint32_t;
std::array<byte, size> bytes;
std::array<word, size / sizeof(word)> words;
};
struct Foo1 {
using Union = MyUnion;
Union u;
static constexpr std::size_t length = Union::size;
};
Why does it matter that Foo1::u is not a static member?
Non-static members can be accessed only within member functions.
I recommend using std::variant instead of union. It's much easier to use.
You can directly get it from type
// direct
consteval static auto length() { return std::tuple_size<decltype(MyUnion::bytes)>::value; }
// with indirection
consteval static auto length() { return std::tuple_size<std::decay_t<decltype(u.bytes)>>::value; }
Or you can do it by create new instance.
// direct
consteval static auto length() { return MyUnion{.bytes={}}.bytes.size(); }
// just the member + indirection
consteval static auto length() { return decltype(u.bytes){}.size(); }
For the errors you got in your code
invalid use of non-static data member 'Foo1::u'
means you cannot use non-static data member u without instance (e.g. inside static member function).
'this' is not a constant expression*
means you cannot call this.length() (simply because it's consteval but this is not here)
Why? because it's what the standard say, otherwise there is no reason to use constexpr specifier at all because compiler can infer it, isn't it?
Related question:
Why is constexpr not automatic?
Why isn't std::array::size static?

Aggregate initialization of anonymous struct with designated initializers

I am porting legacy C++ code to work with GCC 9.2. Using C++20 and GNU extensions are valid options.
The legacy code makes heavy use of anonymous structs nested in unions and aggregate initialization with designated initializes for example:
union u
{
int a;
struct
{
int b;
int c;
};
};
u f = { .b = 1, .c = 2 };
This example does compile with clang -std=gnu++2a -Wall -Wextra, but it does not compile with g++ -std=gnu++2a -Wall -Wextra:
error: too many initializers for 'u'
As there are many cases where such constructs are applied in the code, it would be necessary to apply potential changes to the code in an automated way (for example with the help of regular expressions). How can I compile "this code" with GCC 9.2 by changing the code in an automated way and as little as possible?
By moving the nested structure to the first position within the union and initializing the structure like an non-anonymous structure compiles with g++ -std=gnu++2a -Wall -Wextra:
union u {
struct
{
int b;
int c;
};
int a;
};
u f = { {.b = 1, .c = 2 } };
It should be possible to detect all anonymous structs within unions with regular expressions in the union's definitions. But I do not see how regular expressions can be used to modify the list initialization appropriately.
You can initialize only one member of the union:
because of 15.6.2 [class.base.init] paragraph 8:
An attempt to initialize more than one non-static data member of a union renders the program ill-formed.
so if you put: { .b=1, .c=2 } it sees you are initializing two members.
Then you can initialize either, .b or .c but not both.
Moreover:
If the element is an anonymous union object and the initializer list is a designated-initializer-list, the anonymous union object is initialized by the designated-initializer-list​​{​​D​​}​, where ​D​ is the ​designated-initializer-clause​ naming a member of the anonymous union object. There shall be only one such designated-initializer-clause​.
The key is: where ​D​ is the ​designated-initializer-clause​ naming a member of the anonymous union object
You may get more info here.
To be able to give a value to the members of the struct you should give an element:
union u
{
int a;
struct
{
int b;
int c;
} v; // <-- this
};
u f = { .v = { .b = 1, .c = 2 } };
But this would break the accesses to every u: i.e. f.v.b = 0.
So I think your construct is not compatible with C++20. Why clang compiles and works? May be it is an extension when there are no constructors and/or destructors involved.

Why can I initialize a regular array from {}, but not a std::array

This works:
int arr[10] = {};
All elements of arr are value-initialized to zero.
Why doesn't this work:
std::array<int, 10> arr({});
I get the following warning from g++ (version 4.8.2):
warning: missing initializer for member ‘std::array<int, 10ul>::_M_elems’
There are two issues one which is a matter of style and the warning.
Although it may not be obvious, aggregate initialization is happening on a temporary which is then being used as an argument to the copy constructor. The more idiomatic to do this initialization would be as follows:
std::array<int, 10> arr = {};
Although this still leaves the warning.
The warning is covered by gcc bug report: - -Wmissing-field-initializers relaxation request and one of the comments says:
[...]Surely, the C++ syntax of saying MyType x = {}; should be supported,
as seen here:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
where for instance:
struct S {
int a;
float b;
std::string str;
};
S s = {}; // identical to S s = {0, 0.0, std::string};
That shouldn't warn for the reasons stated in earlier comments.
and a follow-up comment says:
My statement about zero-initialization was inaccurate (thanks), but
the general point still stands: in C you have to write ' = {0}' since
empty-braces initializer is not supported by the language (you get a
warning with -pedantic); in C++, you can write ' = {}' or 'T foo =
T();', but you don't need to write ' = {0}' specifically.
The latest versions of gcc does not produce this warning for this case, see it live working with gcc 5.1.
We can see this topic also covered in the Clang Developers lists in the thead: -Wmissing-field-initializers.
For reference the draft C++11 standard section 8.5.1 [dcl.init.aggr] says:
If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from an empty initializer list (8.5.4). [
Example:
struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of
an expression of the form int(), that is, 0. —end example ]
So as this is valid C++, although as noted using {} is not valid C99. One could argue that it is only a warning, but this seems like idiomatic C++ using {} for aggregate initialization and is problematic if we are using -Werror to turn warnings into errors.
Firstly, you can use the ({}) initializer with an std::array object, but semantically that stands for direct-initialization using copy-constructor from a temporary value-initialized std::array object, i.e. it is equivalent to
std::array<int, 10> arr(std::array<int, 10>{});
And it is actually supposed to compile.
Secondly, you don't really have to go the ({}) way when you can just do
std::array<int, 10> arr = {};
or
std::array<int, 10> arr{};
The first of the two is the most syntactically similar to your int arr[10] = {};, which makes me wonder why you didn't try it at first. Why did you decide to use ({}) instead of = {} when you built the std::array version of = {} syntax?
Enough people have pointed this out as a "problem" when compiling with -Werror that I think it's worth mentioning that the problem goes away if you just double up:
std::array<int, 10> arr{{}};
Does not produce any warning for me on gcc 4.9.2.
To add a bit as to why this solves it: my understanding was that std::array is actually a class with a C array as its only member. So double up on the braces makes sense: the outer braces indicate you are initializing the class, and then the inner braces default initialize the one and only member of the class.
Since there is no possible ambiguity when there is only one variable in a class, just using one pair of {} should be reasonable, but gcc is overly pedantic here, and warns.

Does in class member initialization takes place at compile time or run-time?

In C++11 a new feature was introduced where the programmer can initialize class member variables inside class's definition, see code below:
struct foo
{
int size = 3;
int id = 1;
int type = 2;
unsigned char data[3] = {'1', '2', '3'};
};
Is this initialization takes place during compile time or this feature is just syntactic sugar and member variables are initialized in the default constructor?
First of all yes, as stated before, it is syntactic sugar. But since the rules can be too much to remember, here's a logical experiment to help you figure out what happens in compile time and what not
You have your c++11 class that features in class initializers
struct foo { int size = 3; };
And another class that will help us with our experiment
template<int N>
struct experiment { enum { val = N }; };
Let our hypothesis H0 be that initialization does happen in compile time, then we could write
foo a;
experiment<a.size> b;
No luck, we fail to compile. One could argue that failure is due to foo::size being non constant so lets try with
struct foo { const int size = 3; }; // constexpr instead of const would fail as well
Again, as gcc informs us
the value of ‘a’ is not usable in a constant expression
experiment b;
or (more clearly) visual studio 2013 tells us
error C2975: 'N' : invalid template argument for 'example', expected compile-time constant expression
So, we have to discard H0 and deduce that initialization does not happen in compile time.
What would it take to happen in compile time
There is an old syntax that does the trick
struct foo { static const int size = 3; };
Now this compiles but beware this is (technically and logically) no longer in class initialization.
I had to lie for a little to make a point, but to expose the whole truth now : Message errors imply that a is the real problem. You see, since you have an instance for an object (Daniel Frey also mentions this) memory (for members) has to be initialized (at runtime). If the member was (const) static, as in the final example, then it's not part of the subobjects of a(ny) class and you can have your initialization at compile time.
In-class initialisers for member-variables are syntactic sugar for writing them in the constructor initialiser list, unless there's an explicit initialiser already there, in which case they are ignored.
In-class initialisers of static const members are for constant literals, a definition is still needed (though without initialiser).
C++ has the "as if"-rule from C, so anything resulting in the prescribed observed behavior is allowed.
Specifically, that means static objects may be initialised at compile-time.
It's just syntactic sugar. Also consider that an instance usually means memory which has to be initialized with the correct values. Just because these values are provided with a different syntax does not change the fact that the memory needs to be initialized - which happens at run-time.
Its essentially syntactic sugar for a user provided constructor which initializes the values. You are providing default values for data members. When you ask whether this happens at compile time or run time, the answer depends on the context its used in.
Hopefully, these examples will help. Try them in http://gcc.godbolt.org and see the dissassembly and compilation errors.
struct S { int size = 3; };
//s's data members are compile time constants
constexpr S s = {};
//r's data members are run time constants
const S r = {};
//rr's data members are run time constants,
//but we don't know the values in this translation unit
extern const S rr;
template <int X> class Foo {};
//Ok, s.size is a compile time expression
Foo<s.size> f;
//Error, r.size is not a compile time expression
Foo<r.size> g;
//Compile time expression, this is same as return 3;
int foo() { return s.size; }
//This also works
constexpr int cfoo() { return s.size; }
//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }
//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }
As others have shown, static data members (and global variables, same thing essentially) for primitive types such as ints and floats have some weird rules where they can be const but still be used in compile time contexts as if they were constexpr. This is for backwards compatibility with C and the lack of the constexpr feature in the past. Its unfortunate now because it just makes understanding constexpr and what differentiates run time expressions from compile time expressions more confusing.
Having int size = 3; is exactly equivalent to having int size; and then each constructor that doesn't already have size in its initializer list (including compiler-generated constructors) having size(3) there.
Strictly speaking C++ doesn't have a distinction between "compile-time" and "run-time".

In class initialization and initializer list

I have recently discovered that you cant have at the same time in class initialization and initializer list.
The following code fails :
struct s
{
int i=0;
};
int main() {
s s1; //s1.i = 0
//s s2={42}; //fails
return 0;
}
If I remove the in class initialization, the initializer list works fine !
Can someone explains me why a such thing is no allowed ?
In fact this is allowed in C++14.
struct s
{
int i=0;
};
int main() {
s s1;
s s2 = {42}; // succeeds
}
It's likely that your compiler just isn't implementing the new rule in C++14. The latest version of clang, however, accepts this and does the correct thing in C++14 mode.
When in-class initialization was added to C++11 it was specified such that it prevented a class from being an aggregate. This was done because at the time the aggregate concept was closely related to PoD types which need to be trivially constructible. Having an in-class initialization means that a type is no longer trivially constructible. Since then, however, the two concepts have become more independent, and so for C++14 a short proposal reversing that decision was accepted.
This initialization:
s s1 = { 42 };
requires that s be an aggregate, or that it have a valid constructor taking e.g an int or an std::initializer_list.
When you add a member initialization at the point of declaration, you render your class s a non-aggregate, so you can no longer use aggregate initialization.
You could use the same initialization syntax for your non-aggregate by adding a constructor:
struct s
{
s(int i) : i(i) {}
int i=0;
};
I believe this restriction has been relaxed for C++14.
See What are aggregates... for more information.