{"id":5159,"date":"2023-08-21T18:56:08","date_gmt":"2023-08-22T02:56:08","guid":{"rendered":"https:\/\/wonghoi.humgar.com\/blog\/?page_id=5159"},"modified":"2025-07-08T18:13:16","modified_gmt":"2025-07-09T02:13:16","slug":"programming-concepts-initializer-lists-c","status":"publish","type":"page","link":"https:\/\/wonghoi.humgar.com\/blog\/programming-concepts-initializer-lists-c\/","title":{"rendered":"Programming Concepts: Initializer Lists (C++)"},"content":{"rendered":"\n<p>Many people have taught about the rules to C++&#8217;s initializers list and when they are used, but few have developed a model\/idea to explain why things are the way they are, such as why initializers list always follows the order which the members are declared, not the order declared by the users. <\/p>\n\n\n\n<p>There are just too many rules to remember, but there&#8217;s one easy explanation that you can quickly derive the behavior of initializer and use cases for initializer lists: if you were the compiler, how would you break classes into loose functions and variables (C++ without classes) which eventually what low level implementation (or assembly) sees: code and data?<\/p>\n\n\n\n<p>In C++, you can think of classes = structs + other features recognized by the compiler. This means fundamentally when a struct exist, (non-heap) memory is already allocated for all its members (heap memory are referenced by pointer, so it&#8217;s just an integer data member of the class). <\/p>\n\n\n\n<p>C++ is not Python where you can make up class members as you go (including inside the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">__init__<\/code> constructor) at runtime. This means by the time you get into a C++ constructor, ALL data members are already set up (just like variable declarations in loose\/outside-class code) and you are merely calling a function (constructor is automatically called first) to modify the already established variables (data members).<\/p>\n\n\n\n<p>This is what roughly goes on when you create\/build an object in C++<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n   BLOCK declaring virtual base classes \n   (virtual inheritance blesses all methods with \n   virtuals which means adding an extra vptr to \n   the vtable to the object)\n\n   virtual inheritance makes sure the grandparent\n   is constructed by the child first to be SHARED\n   by both parents in a diamond multiple inheritance\n   setting so there's no ambiguity. This is not\n   the normal order of construction like non-virtual\n   inheritance\n\n   BLOCK declaring non-virtual base classes\n\n   BLOCK declaring ALL data members of A\n\n   BODY of A's constructor\n}<\/pre>\n\n\n\n<p>So if I had to make my own initializer list processor in a pinch, I&#8217;d just write a macro preprocessor which translates parses the list bock {<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">: x(X), y(Y) ...<\/code>} that goes right after the constructor declaration but right before the constructor body, and find the line matching the variables on the list and add {<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">X<\/code>} right after it, to illustrate<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int x;\n\/\/ after parsing x(X) item on initializer list becomes\nint x {X};\n\/\/ where X is a blind? substitution of the phrase<\/pre>\n\n\n\n<!--more-->\n\n\n\n<p>In C++ (also C if the concept came from there), there are a few constructs that doesn&#8217;t allow you to modify variables once it&#8217;s established. Therefore you need to intercept the process of &#8220;establishing the memory for data members of the struct&#8221;, aka the variable declaration block, modified by the initializer list syntax.<\/p>\n\n\n\n<p>Say you have a class A declared like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class A\n{\n  public: \/\/ struct is class with public by default\n    Type1 m1;\n    Type2 m2;\n};\nA a;<\/pre>\n\n\n\n<p>Without initializer list, what this is doing is conceptually equivalent to mangling (adding prefix to the variable names) scoped (namespaced) global variables with an instance prefix (say &#8216;a_&#8217;)<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Type1 A::a_m1;\nType2 A::a_m2;\n\/\/ Constructor of A<\/pre>\n\n\n\n<p>I intentionally use placeholders Type1 and Type2 here because you&#8217;ll realize there are a few types in C++ that cannot be declared without providing an initial value:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>constants must never be changed, so must initialize on creation<\/li>\n\n\n\n<li>references are bound on creation and cannot be reassigned (changed)<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Invalid: consts value must be known at ALL times\nconst bool A::a_m2; \n\/\/ Invalid: refs are bound and sealed on creation\n      int&amp; A::a_m1; <\/pre>\n\n\n\n<p>Inintializer list gives you direct access to the variable declaration process before getting to the constructor where the declaration is already set and done, effectively doing this<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const bool A::a_m2 = x; \n      int&amp; A::a_m1 = y; \n\/\/ Constructor body of A<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct A\n{\n  A(int x, int y) : x(x), y(y) {}\n  int x;\n  int&amp; y;\n};<\/pre>\n\n\n\n<p>If you visualize initializer lists as it simply unrolls as scoped namespace declaration in the open space, it&#8217;s immediately obvious why the language compiler is anal about the construction order which also dictates the dependency on previous members<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const bool A::a_m2 = x; \n      int&amp; A::a_m1 = A::a_m2; \n\/\/ Constructor body of A<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct A\n{\n  A(int X) : x(X), y(x) {}\n  int x;\n  int&amp; y;\n};<\/pre>\n\n\n\n<p>This is the same as with inheritance, with B as the base (parent) class:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct B { int z; };\nstruct A : public B\n{\n  A(int X) : B{7}, x(z+X) {}\n  int x;\n};<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ it's unnamed temporary, \n\/\/ that's why I call it temp_b instead of b\nB::temp_b_z = 7;      \n\n\/\/ Create an alias due to inheritance\nA::a_z&amp; B::temp_b_z;  \n\n\/\/ 7+X where X is the input to the constructor of A\nA::a_x = A::a_z + X   <\/pre>\n\n\n\n<p>It&#8217;s obvious B (the base\/parent) must be established first before the child exist, but this is also the case when we translate initializer lists to open function declaration.<\/p>\n\n\n\n<p>It&#8217;s also obvious from the open function declaration view that if you can put the values you want in the function declaration (initializer lists), doing so saves you an extra step modifying established values, so obviously it has better performance and less stuff to debug.<\/p>\n\n\n\n<p>The other case is when your member objects had no default constructor (not even the implict one, which happens when you declare a multivariate constructor yet didn&#8217;t declare a constructor with zero input arguments). I&#8217;ll skip A&#8217;s constructor for now<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class B\n{\n    B(int P, int Q) {y=P+Q;}\n    int y;\n};\n\nstruct A\n{\n    \/\/ Default constructor method of A goes here\n    int x;\n    B b;\n};<\/pre>\n\n\n\n<p>Since there is no zero-argument (default) constructor for <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">B() {...}<\/code>, this first line is established by default<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int A::a_x;\n\n\/\/ Invalid, no zero-arg\/default constructor B()\nB A::b;  \n\n\/\/ Constructor body of A<\/pre>\n\n\n\n<p>The only way to establish B is to explicitly call the 2-argument constructor <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">B(int, int)<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int A::a_x;\n\n\/\/ Internally sets A::b.y = 7\nB A::b(3, 4);  \n\n\/\/ Constructor body of A<\/pre>\n\n\n\n<p>And b must be built in the order A was declared, which is x first, then b (an instance of class B), then the constructor for A can start.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct B\n{\n    B(int P, int Q) {y=P+Q;}\n    int y;\n};\n\nstruct A\n{\n    A() : B(3, 4) {}\n    int x;\n    B b;\n};<\/pre>\n\n\n\n<p>The situation is exactly the same as the case when class B is the parent\/base, which is the same as the declaration of B being moved to the first line (since parents needs to be established first before child exists). Unlike consts\/references that the initial value needs to be at the declaration because it&#8217;s later unmodifiable, lacking default\/zero-arg constructor means the default attempt to use them at declaration (unless otherwise specified, aka by initializer lists) will fail. <\/p>\n\n\n\n<h2 class=\"wp-block-heading has-text-align-center\">Setting these values must be done at declaration <br>(through initialization lists)<\/h2>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<h2 class=\"wp-block-heading\">Cannot modify after creation<br>(aka in constructor body)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Consts<br><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">const int x = 5;<\/code><\/li>\n\n\n\n<li>References (bound on creation)<br><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">int&amp; x = y;<\/code><\/li>\n<\/ul>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<h2 class=\"wp-block-heading\">Default initialization is not possible<br>(class\/object has no default ctor)<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct B { \n  B(int x, int y) \n    {z=x+y};\n  int z; \n};<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Composition (Member) objects <br><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">class A { B b; };<\/code><\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Inheritance (Parent) class <br><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">class A : public B { \u2026 };<\/code><\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_5159\" class=\"pvc_stats all  \" data-element-id=\"5159\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>Many people have taught about the rules to C++&#8217;s initializers list and when they are used, but few have developed a model\/idea to explain why things are the way they are, such as why initializers list always follows the order &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/programming-concepts-initializer-lists-c\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_5159\" class=\"pvc_stats all  \" data-element-id=\"5159\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"inline_featured_image":false,"footnotes":""},"class_list":["post-5159","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/pages\/5159","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/comments?post=5159"}],"version-history":[{"count":16,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/pages\/5159\/revisions"}],"predecessor-version":[{"id":6543,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/pages\/5159\/revisions\/6543"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=5159"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}