最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - C++17 Are non-static POD class members initialized with curly braces {} set to 0? - Stack Overflow

programmeradmin1浏览0评论

Got code review comment that my non-static class POD members be set to an invalid value. I agree that the initial value should be invalid. For my cases, 0 is invalid. Here's an example:

class A
{
  int a_{};  // Is a_ initialized to 0?
  double b_{}; // Is b_ initialized to 0.0?
  // etc.
};

I added the {} sometimes to handle compiler warnings that these class members should be initialized. I thought they would be initialized to zero. (Empirical observation indicates they are 0, but I am looking for authoritative sources one way or the other.)

I looked at these two links but could not say for certain that {} results in 0.
Meaning of default initialization changed in C++11?
Default and value initialization of POD types in C++

A clear simple source would be useful. I tried finding something here without luck.
++draft/

Got code review comment that my non-static class POD members be set to an invalid value. I agree that the initial value should be invalid. For my cases, 0 is invalid. Here's an example:

class A
{
  int a_{};  // Is a_ initialized to 0?
  double b_{}; // Is b_ initialized to 0.0?
  // etc.
};

I added the {} sometimes to handle compiler warnings that these class members should be initialized. I thought they would be initialized to zero. (Empirical observation indicates they are 0, but I am looking for authoritative sources one way or the other.)

I looked at these two links but could not say for certain that {} results in 0.
Meaning of default initialization changed in C++11?
Default and value initialization of POD types in C++

A clear simple source would be useful. I tried finding something here without luck.
https://eel.is/c++draft/

Share Improve this question edited Nov 20, 2024 at 21:21 user17732522 76.9k3 gold badges81 silver badges144 bronze badges asked Nov 20, 2024 at 20:27 PaulHPaulH 1401 silver badge7 bronze badges 25
  • What exactly are you looking for? {} here is simply a default member initializer like = 0 would be and the initialization simply follows the normal initialization rules just as in a variable definition int a_{}; anywhere else. There is nothing special about the class properties, the storage duration or that it appears in a class at all. – user17732522 Commented Nov 20, 2024 at 20:40
  • 1 "Got code review comment that my non-static class POD members be set to an invalid value.": That's really weird approach btw. The constructor of the class ought to initialize all members into valid states that preserve the class invariant. That's why proper classes, rather than just aggregate structs, are used. There should never be any invalid observable state. Initializing to an invalid state just prevents tools from recognizing the mistake of not correctly initializing to a valid state. – user17732522 Commented Nov 20, 2024 at 20:43
  • > {} here is simply a default member initializer. I would like to know what value a_ and b_ are set to with this {} initializer? I thought the values would be 0 and 0.0 respectively. But the reviewer doesn't think that is the case. (I can change the {} to, say, {-999} and that would satisfy, but we've been using {} for POD types throughout the project. If not zero, then we may need to change all or some of our {}'s. – PaulH Commented Nov 20, 2024 at 20:44
  • 1 @PaulH But you don't fail fast? Fail fast would be to set the compiler to error if any initializer is missing. – user17732522 Commented Nov 20, 2024 at 20:48
  • 2 @BoP Or just completely non-fancy int a_ = 0; and double b_ = 0.0;. But as I mentioned at the end of my answer, {} or maybe cleaner = {} is actually really nice, because it is practically always "correct" and doesn't require me to check the type to understand the intended semantics. – user17732522 Commented Nov 20, 2024 at 21:42
 |  Show 20 more comments

1 Answer 1

Reset to default 6

(I noticed the tags too late. My references are to the final draft for C++23. For C++17 the rules are effectively the same and they have been the same since default member initializers have been introduced in C++11, but there will have been some stuff moved around in the standard texts relative to the references below.)


Assuming the default member initializer {} is actually used, i.e. the used constructor doesn't specify any other initializer for the given member, or, if this is an aggregate class, aggregate initialization doesn't specify any other initializer for the member, then the initialization of these members will be exactly the same as if their declarations appeared in some other context, e.g. as block scope variables. (See especially [class.base.init]/9.1.)

So consider the {} initializer for types int/double. Initialization behavior is specified in long chains of decision rules starting in [dcl.init.general]/16. The first rule that applies is (16.1), because {} is a braced-init-list and all initialization with braces leads to list-initialization according to [dcl.init.list]/3.

The first item to apply in this new decision chain is (3.11), which states that the object is value-initialized (because the initializer list is empty and the type isn't a class type or reference type or any other of the special cases before it in the chain).

Value-initialization is defined in [dcl.init.general]/9 and in this decision chain the last item (9.3) is the first to apply, because neither int nor double are class or array types, and it states that the object is zero-initialized.

Zero-initialization is defined in [dcl.init.general]/6 via another decision chain and there (6.1) already applies, because int and double are scalar types. It states that the object is initialized as if by converting an integer literal 0 to its type.

At this point I hope it is clear that the int member's value will be 0 and double member's value will be 0.0 without going through the implicit conversion rules in [conv] as well.


More generally: {} or, almost equivalently, = {} should in practice always be correct to initialize any object to its canonical "empty" state.

For scalar types it is 0 converted to its type as shown above, for aggregate types it is the equivalent applied to each aggregate element recursively, for non-aggregate class types it is either a call to the default constructor if it is user-provided or full (recursive) zero-initialization followed by default initialization if the default constructor exists, but is not user-provided, or potentially a call to a std::initializer_list constructor with empty initializer list.

So, except for class types with a constructor that is intentionally written to leave the object in an invalid or non-empty state, the result will be a fully initialized object to an "empty" state and if an object has a notion of an empty state that is independent of any inputs, then it should also be possible to initialize it with {}, assuming no evil class design.

The only thing {} might not guarantee is zero-initialization of padding (i.e. as memset would permit for trivially-copyable types). Some forms of initialization do also guarantee that, but it isn't clear what that is even worth, because the standard doesn't specify that padding has stable values at all anyway (in contrast to C).

发布评论

评论列表(0)

  1. 暂无评论