{"id":12413,"date":"2022-01-18T10:24:05","date_gmt":"2022-01-18T18:24:05","guid":{"rendered":"https:\/\/www.learncpp.com\/?p=12413"},"modified":"2025-01-06T15:43:36","modified_gmt":"2025-01-06T23:43:36","slug":"struct-aggregate-initialization","status":"publish","type":"post","link":"https:\/\/www.learncpp.com\/cpp-tutorial\/struct-aggregate-initialization\/","title":{"rendered":"13.8 &#8212; Struct aggregate initialization"},"content":{"rendered":"<p>In the previous lesson (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-structs-members-and-member-selection\/\">13.7 -- Introduction to structs, members, and member selection<\/a>), we talked about how to define structs, instantiate struct objects, and access their members.  In this lesson, we&#8217;ll discuss how structs are initialized.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Data members are not initialized by default<\/p>\n<p>Much like normal variables, data members are not initialized by default.  Consider the following struct:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">#include &lt;iostream&gt;\r\n\r\nstruct Employee\r\n{\r\n    int id; \/\/ note: no initializer here\r\n    int age;\r\n    double wage;\r\n};\r\n\r\nint main()\r\n{\r\n    Employee joe; \/\/ note: no initializer here either\r\n    std::cout &lt;&lt; joe.id &lt;&lt; '\\n';\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Because we have not provided any initializers, when <code>joe<\/code> is instantiated, <code>joe.id<\/code>, <code>joe.age<\/code>, and <code>joe.wage<\/code> will all be uninitialized.  We will then get undefined behavior when we try to print the value of <code>joe.id<\/code>.<\/p>\n<p>However, before we show you how to initialize a struct, let&#8217;s take a short detour.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\"><a name=\"aggregate\"><\/a>What is an aggregate? <a href=\"#aggregate\"><i class=\"fa fa-link\" style=\"font-size: 0.8em;\"><\/i><\/a><\/p>\n<p>In general programming, an <strong>aggregate data type<\/strong> (also called an <strong>aggregate<\/strong>) is any type that can contain multiple data members.  Some types of aggregates allow members to have different types (e.g. structs), while others require that all members must be of a single type (e.g. arrays).<\/p>\n<p>In C++, the definition of an aggregate is narrower and quite a bit more complicated.<\/p>\n<div class=\"cpp-note cpp-lightgraybackground\">\n<p class=\"cpp-note-title cpp-bottomline\">Author&#8217;s note<\/p>\n<p>In this tutorial series, when we use the term &#8220;aggregate&#8221; (or &#8220;non-aggregate&#8221;) we will mean the C++ definition of aggregate.\n<\/p><\/div>\n<div class=\"cpp-note cpp-lightgraybackground\">\n<p class=\"cpp-note-title cpp-bottomline\">For advanced readers<\/p>\n<p>To simplify a bit, an aggregate in C++ is either a C-style array (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-c-style-arrays\/\">17.7 -- Introduction to C-style arrays<\/a>), or a class type (struct, class, or union) that has:<\/p>\n<ul>\n<li>No user-declared constructors (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-constructors\/\">14.9 -- Introduction to constructors<\/a>)\n<\/li>\n<li>No private or protected non-static data members (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/public-and-private-members-and-access-specifiers\/\">14.5 -- Public and private members and access specifiers<\/a>)\n<\/li>\n<li>No virtual functions (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/virtual-functions\/\">25.2 -- Virtual functions and polymorphism<\/a>)\n<\/li>\n<\/ul>\n<p>The popular type <code>std::array<\/code> (<a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-stdarray\/\">17.1 -- Introduction to std::array<\/a>) is also an aggregate.<\/p>\n<p>You can find the precise definition of a C++ aggregate <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/aggregate_initialization\">here<\/a>.\n<\/div>\n<p>The key thing to understand at this point is that structs with only data members are aggregates.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Aggregate initialization of a struct<\/p>\n<p>Because a normal variable can only hold a single value, we only need to provide a single initializer:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">int x { 5 };<\/code><\/pre>\n<p>However, a struct can have multiple members:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};<\/code><\/pre>\n<p>When we define an object with a struct type, we need some way to initialize multiple members at initialization time:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">Employee joe; \/\/ how do we initialize joe.id, joe.age, and joe.wage?<\/code><\/pre>\n<p>Aggregates use a form of initialization called <strong>aggregate initialization<\/strong>, which allows us to directly initialize the members of aggregates.  To do this, we provide an <strong>initializer list<\/strong> as an initializer, which is just a braced list of comma-separated values.<\/p>\n<p>There are 2 primary forms of aggregate initialization:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nint main()\r\n{\r\n    Employee frank = { 1, 32, 60000.0 }; \/\/ copy-list initialization using braced list\r\n    Employee joe { 2, 28, 45000.0 };     \/\/ list initialization using braced list (preferred)\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Each of these initialization forms does a <strong>memberwise initialization<\/strong>, which means each member in the struct is initialized in the order of declaration.  Thus, <code>Employee joe { 2, 28, 45000.0 };<\/code> first initializes <code>joe.id<\/code> with value <code>2<\/code>, then <code>joe.age<\/code> with value <code>28<\/code>, and <code>joe.wage<\/code> with value <code>45000.0<\/code> last.<\/p>\n<div class=\"cpp-note cpp-lightgreenbackground\">\n<p class=\"cpp-note-title cpp-bottomline\">Best practice<\/p>\n<p>Prefer the (non-copy) braced list form when initializing aggregates.\n<\/p><\/div>\n<p>In C++20, we can also initialize (some) aggregates using a parenthesized list of values:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">    Employee robert ( 3, 45, 62500.0 );  \/\/ direct initialization using parenthesized list (C++20)<\/code><\/pre>\n<p>We recommend avoiding this last form as much as possible, as it doesn&#8217;t currently work with aggregates that utilize brace elision (notably <code>std::array<\/code>).<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Missing initializers in an initializer list<\/p>\n<p>If an aggregate is initialized but the number of initialization values is fewer than the number of members, then each member without an explicit initializer is initialized as follows:<\/p>\n<ul>\n<li>If the member has a default member initializer, that is used.\n<\/li>\n<li>Otherwise, the member is copy-initialized from an empty initializer list.  In most cases, this will perform value-initialization on those members (on class types, this will invoke the default constructor even if a list constructor exist).\n<\/li>\n<\/ul>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage { 76000.0 };\r\n    double whatever;\r\n};\r\n\r\nint main()\r\n{\r\n    Employee joe { 2, 28 }; \/\/ joe.whatever will be value-initialized to 0.0\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>In the above example, <code>joe.id<\/code> will be initialized with value <code>2<\/code> and <code>joe.age<\/code> will be initialized with value <code>28<\/code>.  Because <code>joe.wage<\/code> wasn&#8217;t given an explicit initializer but has a default member initializer, <code>joe.wage<\/code> will be initialized to <code>76000.0<\/code>.  And finally, because <code>joe.whatever<\/code> wasn&#8217;t given an explicit initializer, <code>joe.whatever<\/code> is value-initialized to <code>0.0<\/code>.<\/p>\n<div class=\"cpp-note cpp-lightbluebackground\">\n<p class=\"cpp-note-title cpp-bottomline\">Tip<\/p>\n<p>This means we can generally use an empty initialization list to value-initialize all members of the struct:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">Employee joe {}; \/\/ value-initialize all members<\/code><\/pre>\n<\/div>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Overloading <code>operator&lt;&lt;<\/code> to print a struct<\/p>\n<p>In lesson <a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-overloading-the-i-o-operators\/\">13.5 -- Introduction to overloading the I\/O operators<\/a>, we showed how to overload <code>operator&lt;&lt;<\/code> to print an enumeration.  It&#8217;s also useful to overload <code>operator&lt;&lt;<\/code> for structs.<\/p>\n<p>Here&#8217;s the same example as in the previous section, now with an overloaded <code>operator&lt;&lt;<\/code>:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">#include &lt;iostream&gt;\r\n\r\nstruct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const Employee&amp; e)\r\n{\r\n    out &lt;&lt; e.id &lt;&lt; ' ' &lt;&lt; e.age &lt;&lt; ' ' &lt;&lt; e.wage;\r\n    return out;\r\n}\r\n\r\nint main()\r\n{\r\n    Employee joe { 2, 28 }; \/\/ joe.wage will be value-initialized to 0.0\r\n    std::cout &lt;&lt; joe &lt;&lt; '\\n';\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>This prints:<\/p>\n<pre>\n2 28 0\r\n<\/pre>\n<p>We can see that <code>joe.wage<\/code> was indeed value-initialized to <code>0.0<\/code> (which prints as <code>0<\/code>).<\/p>\n<p>Unlike an enumeration, a struct can hold multiple values.  How you format the output (e.g. to separate the values) is entirely up to you.  <\/p>\n<p>The three values output by our overloaded <code>operator&lt;&lt;<\/code> above aren&#8217;t intuitive, as there is no indication of what these values mean.  Let&#8217;s do the same example, but update our output function to be a bit more descriptive:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">#include &lt;iostream&gt;\r\n\r\nstruct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const Employee&amp; e)\r\n{\r\n    out &lt;&lt; \"id: \" &lt;&lt; e.id &lt;&lt; \" age: \" &lt;&lt; e.age &lt;&lt; \" wage: \" &lt;&lt; e.wage;\r\n    return out;\r\n}\r\n\r\nint main()\r\n{\r\n    Employee joe { 2, 28 }; \/\/ joe.wage will be value-initialized to 0.0\r\n    std::cout &lt;&lt; joe &lt;&lt; '\\n';\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>This now prints:<\/p>\n<pre>\nid: 2 age: 28 wage: 0\r\n<\/pre>\n<p>That&#8217;s a little easier to understand.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Const structs<\/p>\n<p>Variables of a struct type can be const (or constexpr), and just like all const variables, they must be initialized.<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Rectangle\r\n{\r\n    double length {};\r\n    double width {};\r\n};\r\n\r\nint main()\r\n{\r\n    const Rectangle unit { 1.0, 1.0 };\r\n    const Rectangle zero { }; \/\/ value-initialize all members\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Designated initializers <span class=\"cpp-section-pill cpp-section-standard\">C++20<\/span><\/p>\n<p>When initializing a struct from a list of values, the initializers are applied to the members in order of declaration.<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Foo\r\n{\r\n    int a {};\r\n    int c {};\r\n};\r\n\r\nint main()\r\n{\r\n    Foo f { 1, 3 }; \/\/ f.a = 1, f.c = 3\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Now consider what would happen if you were to update this struct definition to add a new member that is not the last member:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Foo\r\n{\r\n    int a {};\r\n    int b {}; \/\/ just added\r\n    int c {};\r\n};\r\n\r\nint main()\r\n{\r\n    Foo f { 1, 3 }; \/\/ now, f.a = 1, f.b = 3, f.c = 0\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Now all your initialization values have shifted, and worse, the compiler may not detect this as an error (after all, the syntax is still valid).<\/p>\n<p>To help avoid this, C++20 adds a new way to initialize struct members called <strong>designated initializers<\/strong>.  Designated initializers allow you to explicitly define which initialization values map to which members.  The members can be initialized using list or copy initialization, and must be initialized in the same order in which they are declared in the struct, otherwise a warning or error will result.  Members not designated an initializer will be value initialized.<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Foo\r\n{\r\n    int a{ };\r\n    int b{ };\r\n    int c{ };\r\n};\r\n\r\nint main()\r\n{\r\n    Foo f1{ .a{ 1 }, .c{ 3 } }; \/\/ ok: f1.a = 1, f1.b = 0 (value initialized), f1.c = 3\r\n    Foo f2{ .a = 1, .c = 3 };   \/\/ ok: f2.a = 1, f2.b = 0 (value initialized), f2.c = 3\r\n    Foo f3{ .b{ 2 }, .a{ 1 } }; \/\/ error: initialization order does not match order of declaration in struct\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<div class=\"cpp-note cpp-lightyellowbackground\">\n<p class=\"cpp-note-title cpp-bottomline\">For Clang users<\/p>\n<p>When doing designated initializers of single values using braces, Clang improperly issues warning &#8220;braces around scalar initializer&#8221;.  Hopefully this will be fixed soon.\n<\/p><\/div>\n<p>Designated initializers are nice because they provide some level of self-documentation and help ensure you don&#8217;t inadvertently mix up the order of your initialization values.  However, designated initializers also clutter up the initializer list significantly, so we won&#8217;t recommend their use as a best practice at this time.<\/p>\n<p>Also, because there&#8217;s no enforcement that designated initializers are being used consistently everywhere an aggregate is initialized, it&#8217;s a good idea to avoid adding new members to the middle of an existing aggregate definition, to avoid the risk of initializer shifting.<\/p>\n<div class=\"cpp-note cpp-lightgreenbackground\">\n<p class=\"cpp-note-title cpp-bottomline\">Best practice<\/p>\n<p>When adding a new member to an aggregate, it&#8217;s safest to add it to the bottom of the definition list so the initializers for other members don&#8217;t shift.\n<\/p><\/div>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Assignment with an initializer list<\/p>\n<p>As shown in the prior lesson, we can assign values to members of structs individually:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nint main()\r\n{\r\n    Employee joe { 1, 32, 60000.0 };\r\n\r\n    joe.age  = 33;      \/\/ Joe had a birthday\r\n    joe.wage = 66000.0; \/\/ and got a raise\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>This is fine for single members, but not great when we want to update many members.  Similar to initializing a struct with an initializer list, you can also assign values to structs using an initializer list (which does memberwise assignment):<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nint main()\r\n{\r\n    Employee joe { 1, 32, 60000.0 };\r\n    joe = { joe.id, 33, 66000.0 }; \/\/ Joe had a birthday and got a raise\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Note that because we didn&#8217;t want to change <code>joe.id<\/code>, we needed to provide the current value for <code>joe.id<\/code> in our list as a placeholder, so that memberwise assignment could assign <code>joe.id<\/code> to <code>joe.id<\/code>.  This is a bit ugly.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Assignment with designated initializers <span class=\"cpp-section-pill cpp-section-standard\">C++20<\/span><\/p>\n<p>Designated initializers can also be used in a list assignment:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">struct Employee\r\n{\r\n    int id {};\r\n    int age {};\r\n    double wage {};\r\n};\r\n\r\nint main()\r\n{\r\n    Employee joe { 1, 32, 60000.0 };\r\n    joe = { .id = joe.id, .age = 33, .wage = 66000.0 }; \/\/ Joe had a birthday and got a raise\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>Any members that aren&#8217;t designated in such an assignment will be assigned the value that would be used for value initialization.  If we hadn&#8217;t have specified a designated initializer for <code>joe.id<\/code>, <code>joe.id<\/code> would have been assigned the value 0.<\/p>\n<p class=\"cpp-section cpp-topline\" style=\"clear: both\">Initializing a struct with another struct of the same type<\/p>\n<p>A struct may also be initialized using another struct of the same type:<\/p>\n<pre class=\"language-cpp line-numbers\"><code class=\"language-cpp match-braces\">#include &lt;iostream&gt;\r\n\r\nstruct Foo\r\n{\r\n    int a{};\r\n    int b{};\r\n    int c{};\r\n};\r\n\r\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const Foo&amp; f)\r\n{\r\n    out &lt;&lt; f.a &lt;&lt; ' ' &lt;&lt; f.b &lt;&lt; ' ' &lt;&lt; f.c;\r\n    return out;\r\n}\r\n\r\nint main()\r\n{\r\n    Foo foo { 1, 2, 3 };\r\n\r\n    Foo x = foo; \/\/ copy-initialization\r\n    Foo y(foo);  \/\/ direct-initialization\r\n    Foo z {foo}; \/\/ direct-list-initialization\r\n\r\n    std::cout &lt;&lt; x &lt;&lt; '\\n';\r\n    std::cout &lt;&lt; y &lt;&lt; '\\n';\r\n    std::cout &lt;&lt; z &lt;&lt; '\\n';\r\n\r\n    return 0;\r\n}<\/code><\/pre>\n<p>The above prints:<\/p>\n<pre>\n1 2 3\r\n1 2 3\r\n1 2 3\r\n<\/pre>\n<p>Note that this uses the standard forms of initialization that we&#8217;re familiar with (copy, direct, or direct-list-initialization) rather than aggregate initialization.<\/p>\n<p>This is most commonly seen when initializing a struct with the return value of a function that returns a struct of the same type.  We cover this in more detail in lesson <a href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/passing-and-returning-structs\/\">13.10 -- Passing and returning structs<\/a>.<\/p>\n<div class=\"prevnext\"><div class=\"prevnext-inline\">\n\t<a class=\"nav-link\" href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/default-member-initialization\/\">\n <div class=\"nav-button nav-button-next\">\n    <div class=\"nav-button-icon\"><i class=\"fa fa-chevron-circle-right\" aria-hidden=\"true\"><\/i><\/div>\n    <div class=\"nav-button-text\">\n      <div class=\"nav-button-title\">Next lesson<\/div>\n      <div class=\"nav-button-lesson\">\n        <span class=\"nav-button-lesson-number\">13.9<\/span>Default member initialization\n      <\/div>\n    <\/div>\n  <\/div><\/a>\n  \t<a class=\"nav-link\" href=\"\/\">\n  <div class=\"nav-button nav-button-index\">\n    <div class=\"nav-button-icon\"><i class=\"fa fa-home\" aria-hidden=\"true\"><\/i><\/div>\n    <div class=\"nav-button-text\">\n      <div class=\"nav-button-title\">Back to table of contents<\/div>\n    <\/div>\n<\/div><\/a>\n  \t<a class=\"nav-link\" href=\"https:\/\/www.learncpp.com\/cpp-tutorial\/introduction-to-structs-members-and-member-selection\/\">\n  <div class=\"nav-button nav-button-prev\">\n    <div class=\"nav-button-icon\"><i class=\"fa fa-chevron-circle-left\" aria-hidden=\"true\"><\/i><\/div>\n    <div class=\"nav-button-text\">\n      <div class=\"nav-button-title\">Previous lesson<\/div>\n      <div class=\"nav-button-lesson\">\n        <span class=\"nav-button-lesson-number\">13.7<\/span>Introduction to structs, members, and member selection\n      <\/div>\n    <\/div>\n  <\/div><\/a>\n  <\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>In the previous lesson (), we talked about how to define structs, instantiate struct objects, and access their members. In this lesson, we&#8217;ll discuss how structs are initialized. Data members are not initialized by default Much like normal variables, data members are not initialized by default. Consider the following struct: &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[],"_links":{"self":[{"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/posts\/12413"}],"collection":[{"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/comments?post=12413"}],"version-history":[{"count":40,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/posts\/12413\/revisions"}],"predecessor-version":[{"id":18093,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/posts\/12413\/revisions\/18093"}],"wp:attachment":[{"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/media?parent=12413"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/categories?post=12413"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.learncpp.com\/wp-json\/wp\/v2\/tags?post=12413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}