The Most Confusing C++ Behavior
What does this program print out? I use placement new here so you could see when members are initializing (or not).#include <new>
#include <cstdio>
#include <type_traits>
struct A { int a=0; };
struct B { int b{}; };
struct C { int c; C(int c) : c(c) { } };
struct D { int d; D(int d) : d(d) { } D()=default; };
struct E { D e{}; };
int main(int argc, char*argv[])
{
int data;
data = -1; auto&a = * new(&data) A;
printf("A %d %d\n", std::is_trivially_constructible<A>::value, a.a);
data = -2; auto&b = * new(&data) B;
printf("B %d %d\n", std::is_trivially_constructible<B>::value, b.b);
data = -3; auto&c = * new(&data) C{1};
printf("C %d %d\n", std::is_trivially_constructible<C>::value, c.c);
data = -4; auto&d = * new(&data) D;
printf("D %d %d\n", std::is_trivially_constructible<D>::value, d.d);
data = -5; auto&e = * new(&data) E;
printf("E %d %d\n", std::is_trivially_constructible<E>::value, e.e);
}
The output of the above is
A 0 0 B 0 0 C 0 1 D 1 -4 E 0 0
If you want D to initialize with zeros, you need to remember to write `{}`, as E does. The easiest way to not have uninitialized variables is to assign a value inside the class/struct on the lines you declared the variable. You can use is_trivially_constructiblely_copyable, to know if a type can not be memcpy, but you can't really know it's ok to memcpy...
The thing about trivial types is, T* and T& are trivial. You may not want to memcpy (or write to disk/network) structs using those. spans are trivial, but unique_ptr and containers are not. You might not want to bother checking if a type is trivially constructable/copyable unless you're prepared to be a C++ expert, and your codebase is small enough that you're comfortable debugging any bugs caused by pointers and refs getting into places where you didn't mean to.
struct F { A a; int f; };
long long data2;
data2 = -6; auto&f = * new(&data2) F;
printf("F %d %d %d\n", std::is_trivially_constructible<F>::value, f.a.a, f.f);
F is not trivially constructible, yet F::f isn't initialized. F::a is, but F::f is not. However, if you change the above to `F{};` everything will be initialized. Is everything clear now?
Final thought: For everyone's sanity, have initializing all variables be a rule, so no one breaks it unless they have a really good reason to.