{"id":5417,"date":"2018-04-04T05:11:49","date_gmt":"2018-04-04T05:11:49","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-more-rules-to-performance\/"},"modified":"2023-06-26T11:53:17","modified_gmt":"2023-06-26T11:53:17","slug":"c-core-guidelines-more-rules-to-performance","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-more-rules-to-performance\/","title":{"rendered":"C++ Core Guidelines: More Rules about Performance"},"content":{"rendered":"<p>In this post, I continue my journey through the rules to performance in the C++ Core Guidelines.&nbsp; I will mainly write about design for optimization.<\/p>\n<p><!--more--><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5415\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/athlete-bicycle-bike-12838.jpg\" alt=\"\" width=\"600\" height=\"374\" style=\"display: block; margin-left: auto; margin-right: auto;\" data-alt=\"athlete bicycle bike 12838\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/athlete-bicycle-bike-12838.jpg 936w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/athlete-bicycle-bike-12838-300x187.jpg 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/athlete-bicycle-bike-12838-768x478.jpg 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Here are the two rules for today.&nbsp;<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/isocpp\/CppCoreGuidelines\/blob\/master\/CppCoreGuidelines.md#Rper-efficiency\">Per.7: Design to enable optimization<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/isocpp\/CppCoreGuidelines\/blob\/master\/CppCoreGuidelines.md#Rper-type\">Per.10: Rely on the static type system<\/a><\/li>\n<\/ul>\n<h3><a href=\"https:\/\/github.com\/isocpp\/CppCoreGuidelines\/blob\/master\/CppCoreGuidelines.md#Rper-efficiency\">Per.7: Design to enable optimization<\/a><\/h3>\n<p>When I read this title, I immediately have to think about move semantics. Why? Because you should write your algorithms with move semantics and not with copy semantics. You will automatically get a few benefits.&nbsp;<\/p>\n<ol>\n<li>Of course, your algorithms use a cheap move instead of an expensive copy.&nbsp;<\/li>\n<li>Your algorithm is way more stable because it requires no memory, and you will get no <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/memory\/new\/bad_alloc\">std::bad_alloc<\/a>&nbsp;exception.<\/li>\n<li>You can use your algorithm with move-only types such as<a href=\"http:\/\/en.cppreference.com\/w\/cpp\/memory\/unique_ptr\"> std::unique_ptr<\/a>.&nbsp;<\/li>\n<\/ol>\n<p>Understood! Let me implement a generic <span style=\"font-family: 'courier new', courier;\">swap<\/span> algorithm that uses move semantics.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ swap.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;algorithm&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;cstddef&gt; <\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;vector&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>                                                <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> swap(T<span style=\"color: #555555;\">&amp;<\/span> a, T<span style=\"color: #555555;\">&amp;<\/span> b) noexcept {\r\n    T tmp(std<span style=\"color: #555555;\">::<\/span>move(a));\r\n    a <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>move(b);\r\n    b <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>move(tmp);\r\n}\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">BigArray<\/span>{\r\n\r\n<span style=\"color: #9999ff;\">public:<\/span>\r\n    BigArray(std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">size_t<\/span> sz)<span style=\"color: #555555;\">:<\/span> size(sz), data(<span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[size]){}\r\n\r\n    BigArray(<span style=\"color: #006699; font-weight: bold;\">const<\/span> BigArray<span style=\"color: #555555;\">&amp;<\/span> other)<span style=\"color: #555555;\">:<\/span> size(other.size), data(<span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[other.size]){\r\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Copy constructor\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n        std<span style=\"color: #555555;\">::<\/span>copy(other.data, other.data <span style=\"color: #555555;\">+<\/span> size, data);\r\n    }\r\n    \r\n    BigArray<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">=<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> BigArray<span style=\"color: #555555;\">&amp;<\/span> other){                      <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Copy assignment\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n        <span style=\"color: #006699; font-weight: bold;\">if<\/span> (<span style=\"color: #006699; font-weight: bold;\">this<\/span> <span style=\"color: #555555;\">!=<\/span> <span style=\"color: #555555;\">&amp;<\/span>other){\r\n            <span style=\"color: #006699; font-weight: bold;\">delete<\/span> [] data;\r\n            data <span style=\"color: #555555;\">=<\/span> nullptr;\r\n\t\t\t\r\n            size <span style=\"color: #555555;\">=<\/span> other.size;\r\n            data <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[size];\r\n            std<span style=\"color: #555555;\">::<\/span>copy(other.data, other.data <span style=\"color: #555555;\">+<\/span> size, data);\r\n        }\r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>;\r\n    }\r\n    \r\n    <span style=\"color: #555555;\">~<\/span>BigArray(){\r\n        <span style=\"color: #006699; font-weight: bold;\">delete<\/span>[] data;\r\n    }\r\n<span style=\"color: #9999ff;\">private:<\/span>\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">size_t<\/span> size;\r\n    <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">*<\/span> data;\r\n};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>(){\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n  BigArray bigArr1(<span style=\"color: #ff6600;\">2011<\/span>);\r\n  BigArray bigArr2(<span style=\"color: #ff6600;\">2017<\/span>);\r\n  swap(bigArr1, bigArr2);                                           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n};\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Fine. That was it. No! My coworker gave me his type <span style=\"font-family: 'courier new', courier;\">BigArray<\/span>. <span style=\"font-family: 'courier new', courier;\">BigArray<\/span> has a few flaws. I will write about the copy assignment operator (1) later. First of all, I have a more serious concern. <span style=\"font-family: 'courier new', courier;\">BigArray<\/span> does not support move semantics but only copy semantics. What will happen if I swap the <span style=\"font-family: 'courier new', courier;\">BigArrays<\/span> in line (2)?&nbsp; My <span style=\"font-family: 'courier new', courier;\">swap<\/span> algorithm uses move semantic (3) under the hood. Let&#8217;s try it out.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5416\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/swap.png\" alt=\"swap\" width=\"300\" height=\"222\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/swap.png 491w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/04\/swap-300x222.png 300w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Nothing bad will happen. Traditional copy semantics will kick in, and you will get the classical behavior. Copy semantics is a kind of fallback to move semantics. You can see it the other way around. The move is an optimized copy.&nbsp;<\/p>\n<p>How is that possible? I asked for a move operation in my <span style=\"font-family: 'courier new', courier;\">swap<\/span> algorithm. The reason is that <span style=\"font-family: 'courier new', courier;\">std::move<\/span> returns a rvalue. A const lvalue reference can bind to an rvalue, and the copy constructor or a copy assignment operator takes a const lvalue reference. If <span style=\"font-family: 'courier new', courier;\">BigArray<\/span> had a move constructor or a move assignment operator taking rvalue references, both would have higher priority than the copy pendants.&nbsp;&nbsp;<\/p>\n<p>Implementing your algorithms with move semantic means that move semantic will automatically kick in if your data types support it. If not, copy semantics will be used as a fallback. In the worst case, you will have classical behavior.<\/p>\n<p>I said the copy assignment operator has a few flaws. Here are they:<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">BigArray<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">=<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> BigArray<span style=\"color: #555555;\">&amp;<\/span> other){                      \r\n    <span style=\"color: #006699; font-weight: bold;\">if<\/span> (<span style=\"color: #006699; font-weight: bold;\">this<\/span> <span style=\"color: #555555;\">!=<\/span> <span style=\"color: #555555;\">&amp;<\/span>other){                                 <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n        <span style=\"color: #006699; font-weight: bold;\">delete<\/span> [] data;                                        \r\n        data <span style=\"color: #555555;\">=<\/span> nullptr;\r\n\t\t\t\r\n        size <span style=\"color: #555555;\">=<\/span> other.size;\r\n        data <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[size];                            <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n        std<span style=\"color: #555555;\">::<\/span>copy(other.data, other.data <span style=\"color: #555555;\">+<\/span> size, data);  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n    }\r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>;\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<ol>\n<li>I have to check for self-assignment. Most of the time, self-assignment will not happen, but I always check for the special case.<\/li>\n<li>If the allocation fails, this was already modified. The size is <span style=\"font-family: 'courier new', courier;\">wrong<\/span>, and <span style=\"font-family: 'courier new', courier;\">the data<\/span> is already deleted. This means the copy constructor only guarantees the basic exception guarantee but not the strong one. The basic exception guarantee states that there is no leak after an exception. The strong exception guarantees that the program can be rolled back to the state in case of an exception. Read the Wikipedia article about exception safety for more details on <a href=\"https:\/\/en.wikipedia.org\/wiki\/Exception_safety\">exception safety<\/a>.<\/li>\n<li>The line is identical to the line in the copy constructor.<\/li>\n<\/ol>\n<p>You can overcome these flaws by implementing your swap function. This is already suggested by the C++ Core Guidelines:&nbsp;<a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rc-swap\" style=\"color: #268bd2; text-decoration: underline;\">C.83: For value-like types, consider providing a&nbsp;<code class=\"highlighter-rouge no-highlight\" style=\"font-family: 'Roboto Mono', monospace; padding: 0.2em; font-size: 18px; background-color: #f9f9f9;\">noexcept<\/code>&nbsp;swap function<\/a>. Here is the new <span style=\"font-family: 'courier new', courier;\">BigArray<\/span> having a non-member <span style=\"font-family: 'courier new', courier;\">swap<\/span> function and a copy assignment operator using the <span style=\"font-family: 'courier new', courier;\">swap<\/span> function.&nbsp;<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">BigArray<\/span>{\r\n\r\n<span style=\"color: #9999ff;\">public:<\/span>\r\n    BigArray(std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">size_t<\/span> sz)<span style=\"color: #555555;\">:<\/span> size(sz), data(<span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[size]){}\r\n\r\n    BigArray(<span style=\"color: #006699; font-weight: bold;\">const<\/span> BigArray<span style=\"color: #555555;\">&amp;<\/span> other)<span style=\"color: #555555;\">:<\/span> size(other.size), data(<span style=\"color: #006699; font-weight: bold;\">new<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span>[other.size]){\r\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Copy constructor\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n        std<span style=\"color: #555555;\">::<\/span>copy(other.data, other.data <span style=\"color: #555555;\">+<\/span> size, data);\r\n    }\r\n\t\r\n    BigArray<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">=<\/span> (BigArray other){                  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n        swap(<span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>, other);                                 \r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>;\r\n    }\r\n    \r\n    <span style=\"color: #555555;\">~<\/span>BigArray(){\r\n        <span style=\"color: #006699; font-weight: bold;\">delete<\/span>[] data;\r\n    }\r\n\t\r\n    <span style=\"color: #006699; font-weight: bold;\">friend<\/span> <span style=\"color: #007788; font-weight: bold;\">void<\/span> swap(BigArray<span style=\"color: #555555;\">&amp;<\/span> first, BigArray<span style=\"color: #555555;\">&amp;<\/span> second){    <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n        std<span style=\"color: #555555;\">::<\/span>swap(first.size, second.size);\r\n        std<span style=\"color: #555555;\">::<\/span>swap(first.data, second.data);\r\n    }\r\n\t\r\n<span style=\"color: #9999ff;\">private:<\/span>\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">size_t<\/span> size;\r\n    <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">*<\/span> data;\r\n};\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The swap function inline (1) is not a member; therefore, a call<span style=\"font-family: 'courier new', courier;\"> swap(bigArray1, bigArray2)<\/span>&nbsp;uses it.&nbsp; The signature of the copy assignment operator in line (2) may surprise you. Because of the copy, no self-assignment test is necessary. Additionally, the strong exception guarantee holds, and there is no code duplication. This technique is called the <a href=\"https:\/\/en.wikibooks.org\/wiki\/More_C++_Idioms\/Copy-and-swap\">copy-and-swap idiom<\/a>.<\/p>\n<p>There are a lot of overloaded versions of <span style=\"font-family: 'courier new', courier;\">std::swap<\/span> available. The C++ standard provides about 50 overloads.&nbsp;<\/p>\n<\/p>\n<h3><a href=\"https:\/\/github.com\/isocpp\/CppCoreGuidelines\/blob\/master\/CppCoreGuidelines.md#Rper-type\">Per.10: Rely on the static type system<\/a><\/h3>\n<p>This is a kind of meta-rule in C++. Catch errors at compile-time. I can make my explanation of this rule relatively short because I have already written a few articles on this critical topic:<\/p>\n<ul>\n<li>Use automatic type deduction with auto (<a href=\"https:\/\/www.modernescpp.com\/index.php\/automatically-inititialized\">automatically initialized<\/a>) combined with {}-initialization, and you will get many benefits.\n<ol>\n<li>The compiler always knows the right type: <span style=\"font-family: 'courier new', courier;\">auto f = 5.0f.<\/span><\/li>\n<li>You can never forget to initialize a type: <span style=\"font-family: 'courier new', courier;\">auto a<\/span>; will not work.<\/li>\n<li>You can verify with {}-initialization that no narrowing conversion will kick in; therefore, you can guarantee that the automatically deduced type is the type you expected: <span style=\"font-family: 'courier new', courier;\">int i = {f}<\/span>; The compiler will check in this expression that f is, in this case, an <span style=\"font-family: 'courier new', courier;\">int<\/span>. If not, you will get a warning. This will not happen without braces:<span style=\"font-family: 'courier new', courier;\"> int i = f<\/span>;.<\/li>\n<\/ol>\n<\/li>\n<li>Check with <a href=\"https:\/\/www.modernescpp.com\/index.php\/statically-checked\">static_assert<\/a>&nbsp;and the <a href=\"https:\/\/www.modernescpp.com\/index.php\/tag\/type-traits\">type-traits library<\/a> type properties at compile time. If the check fails, you will get a compile-time error: <span style=\"font-family: 'courier new', courier;\">static_assert&lt;std::is_integral&lt;T&gt;::value, &#8220;T should be an integral type!&#8221;).<\/span><\/li>\n<li>Make type-safe arithmetic with the user-defined literals and the new built-in literals(<a href=\"https:\/\/www.modernescpp.com\/index.php\/tag\/benutzerdefinierte-literale\">user-defined literals<\/a>): <span style=\"font-family: 'courier new', courier;\">auto distancePerWeek=&nbsp; (5 * 120_km + 2 * 1500m &#8211; 5 * 400m) \/ 5;.&nbsp;&nbsp;<\/span><\/li>\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/component\/content\/article\/41-blog\/embedded\/222-override-and-final?Itemid=239\"><span style=\"font-family: 'courier new', courier;\">override<\/span> and <span style=\"font-family: 'courier new', courier;\">final<\/span><\/a>&nbsp;provide guarantees to virtual methods. The compiler checks with <span style=\"font-family: 'courier new', courier;\">override<\/span> that you overrode a virtual method. The compiler guarantees further with <span style=\"font-family: 'courier new', courier;\">final<\/span> that you can not override a virtual method that is declared <span style=\"font-family: 'courier new', courier;\">final<\/span>.&nbsp;<\/li>\n<li><span style=\"color: #444444; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; font-style: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; background-color: #ffffff; float: none;\"><a href=\"https:\/\/www.modernescpp.com\/index.php\/tag\/nullptr\">The New Null Pointer Constant nullptr<\/a>&nbsp;cleans in C++11 up with the ambiguity of 0 and the macro NULL.<\/span><\/li>\n<\/ul>\n<h2>What&#8217;s next?<\/h2>\n<p>My journey through the rules to performance will go on. In the <a href=\"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-the-remaining-rules-to-performance\">next post<\/a>, I will, in particular, write about how to move computation from runtime to compile-time and how you should access memory.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, I continue my journey through the rules to performance in the C++ Core Guidelines.&nbsp; I will mainly write about design for optimization.<\/p>\n","protected":false},"author":21,"featured_media":5415,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[372],"tags":[470],"class_list":["post-5417","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-modern-c","tag-performance"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5417","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=5417"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5417\/revisions"}],"predecessor-version":[{"id":6832,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5417\/revisions\/6832"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5415"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}