1

If I have the following struct

struct test
{
    char *x;
    std::string y;
};

And I initialize with

test *t = new test();

That should value-initialize the object and do the following based on the standard:

if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;

struct test has a non-trivial constructor, this can be checked with:

static_assert(std::is_trivially_constructible<test>::value, "test is not is_trivially_constructible");`)

Does the standard imply that my test object should always be zero-initialized in the case of value-initialization, and then subsequently default-initialized?

And should I be able to reliably assume that after doing test *t = new test() if I immediately check t->x == nullptr, that should be true because char *x (a pointer / scalar type) should get zero-initialized during value-initialization of test.

I ask because Coverity gives a Type: Uninitialized pointer read (UNINIT) warning because it reports the following if you try do something like if (t->x) after value-intialization:

Assigning: "t" = "new test", which is allocated but not initialized.

Is Coverity misinterpreting the standard as "value-initialization if trivial constructor OR default-initialization if non-trivial constructor"? If I remove the std::string y; member so that test has a trivial-default-constructor, Coverity no longer has a warning and assumes the char *x member is zero-initialized.

For what it's worth, I'm just using g++ -O3 -std=c++17 to compile and I have not been able to create an actual scenario where zero-intialization doesn't happen for my test object.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
George
  • 35
  • 2
  • The warning is correct for C++98, but not any later revisions. Maybe there is a setting to ignore C++98-only issues? Also the message is suspect. It doesn't include the `()` initializer which makes all the difference here. (That it makes all the difference seems very fragile btw. It could be made much safer without losing anything by just writing `char *x{};` in the class definition.) – user17732522 Jul 12 '22 at 14:33
  • Don't use new when you don't have to. If you need a pointer to leverage (dynamic) polymorphism use std::make_unique. Otherwhise work with instances and references – Pepijn Kramer Jul 12 '22 at 15:38
  • Here is a nice article on the topic by [Sy Brand](https://stackoverflow.com/users/496161/tartanllama) with telling title [Initialization in C++ is bonkers](https://blog.tartanllama.xyz/initialization-is-bonkers/) – Alexander Malakhov Jul 12 '22 at 18:34

1 Answers1

1

The warning is not correct for modern C++(including C++17) as explained below.

should I be able to reliably assume that after doing test *t = new test(); if I immediately check t->x == nullptr, that should be true because char *x (a pointer / scalar type) should get zero-initialized during value-initialization of test.

Yes, it is guaranteed by the standard that x is zero-initialized and hence the check t->x == nullptr must evaluate to true. This can be seen from dcl.init#6 which states:

To zero-initialize an object or reference of type T means:

  • if T is a (possibly cv-qualified) non-union class type, its padding bits are initialized to zero bits and each non-static data member, each non-virtual base class subobject, and, if the object is not a base class subobject, each virtual base class subobject is zero-initialized;

(emphasis mine)

Jason
  • 36,170
  • 5
  • 26
  • 60