{"id":6261,"date":"2021-12-08T20:41:15","date_gmt":"2021-12-08T20:41:15","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/the-type-traits-library\/"},"modified":"2023-06-26T09:21:10","modified_gmt":"2023-06-26T09:21:10","slug":"the-type-traits-library","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/the-type-traits-library\/","title":{"rendered":"The Type-Traits Library: Correctness"},"content":{"rendered":"<p>The two main goals of the type-traits library are compelling: correctness and optimization. Today, I write about correctness.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6251\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/11\/templatesTypeTraits.png\" alt=\"templatesTypeTraits\" width=\"650\" height=\"412\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/11\/templatesTypeTraits.png 918w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/11\/templatesTypeTraits-300x190.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/11\/templatesTypeTraits-768x487.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>The type-traits library enables it to type queries, comparisons, and modifications at compile time. In my previous post about the type traits library, I only wrote about type queries and comparisons. Before I write about the correctness aspect of the type-traits library, I want to write a few words about type modifications.<\/p>\n<h2>Type Modifications<\/h2>\n<p>The type-traits library offers many metafunctions to manipulate types. Here are the most interesting ones.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/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;\">\/\/ const-volatile modifications:<\/span>\r\nremove_const;\r\nremove_volatile;\r\nremove_cv;\r\nadd_const;\r\nadd_volatile;\r\nadd_cv;\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ reference modifications:<\/span>\r\nremove_reference;\r\nadd_lvalue_reference;\r\nadd_rvalue_reference;\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ sign modifications:<\/span>\r\nmake_signed;\r\nmake_unsigned;\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ pointer modifications:<\/span>\r\nremove_pointer;\r\nadd_pointer;\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/\/ other transformations:<\/span>\r\ndecay;\r\nenable_if;\r\nconditional;\r\ncommon_type;\r\nunderlying_type;\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>To get an <code>int<\/code> from an <code>int<\/code> or a<code> const int<\/code>, you have to ask for the type with <code>::type<\/code>.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/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%;\">std<span style=\"color: #555555;\">::<\/span>is_same<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span>, std<span style=\"color: #555555;\">::<\/span>remove_const<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;::<\/span>type<span style=\"color: #555555;\">&gt;::<\/span>value; <span style=\"color: #0099ff; font-style: italic;\">\/\/ true<\/span>\r\nstd<span style=\"color: #555555;\">::<\/span>is_same<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span>, std<span style=\"color: #555555;\">::<\/span>remove_const<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;::<\/span>type<span style=\"color: #555555;\">&gt;::<\/span>value; <span style=\"color: #0099ff; font-style: italic;\">\/\/ true<\/span>\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Since C++14, you can use<code> _t<\/code> to get the type, such as with <code>std::remove_const_t<\/code>:<\/p>\n<p><!-- HTML generated using hilite.me --><\/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%;\">std<span style=\"color: #555555;\">::<\/span>is_same<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span>, std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">remove_const_t<\/span><span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;&gt;::<\/span>value; <span style=\"color: #0099ff; font-style: italic;\">\/\/ true<\/span>\r\nstd<span style=\"color: #555555;\">::<\/span>is_same<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span>, std<span style=\"color: #555555;\">::<\/span><span style=\"color: #007788; font-weight: bold;\">remove_const_t<\/span><span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;&gt;::<\/span>value; <span style=\"color: #0099ff; font-style: italic;\">\/\/ true<\/span>\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>To get an idea of how helpful these metafunctions from the type-traits library are, here are a few examples.<\/p>\n<ul>\n<li><strong><code>std::decay<\/code><\/strong>:<code> std::thread<\/code> applies<code> std::decay<\/code> to its arguments. The arguments of<code> std::thread<\/code> , including the executed function<code> f<\/code> and their arguments <code>args<\/code>. Decay means that implicit conversions from array-to-pointer, function-to-pointer is performed, and<code> const\/volatile<\/code> qualifiers and references are removed.<\/li>\n<li><strong><code>std::enable_if<\/code><\/strong> is a convenient way to use SFINAE. SFINAE stands for Substitution Failure Is Not An Error and applies during overload resolution of a function template. If substituting the template parameter fails, the specialization is discarded from the overload set, but this failure causes no compiler error.<\/li>\n<li><strong><code>std::conditional<\/code><\/strong> is the ternary operator at compile time?<\/li>\n<li><strong><code>std::common_type<\/code><\/strong> determines the common type among all types to which all types can be converted.<\/li>\n<li><strong><code>std::underlying_type<\/code><\/strong> determines the type of an enum.<\/li>\n<\/ul>\n<p>Maybe, you are not convinced about the benefit of the type traits library. Let me end my series of posts to the type-traits library with its two main goals: correctness and optimization.<\/p>\n<\/p>\n<h2>Correctness<\/h2>\n<p>Correctness means that you can use the type-traits library in C++11 to make your algorithm safer. The following implementation of the gcd algorithm requires that the binary modulo operator is valid for its arguments.<\/p>\n<p><!-- HTML generated using hilite.me --><\/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;\">\/\/ gcd2.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;type_traits&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>\r\nT gcd(T a, T b) {\r\n    static_assert(std<span style=\"color: #555555;\">::<\/span>is_integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value, <span style=\"color: #cc3300;\">\"T should be an integral type!\"<\/span>); <span style=\"color: #0099ff;\"> \/\/ (1)<\/span>\r\n    <span style=\"color: #006699; font-weight: bold;\">if<\/span>( b <span style=\"color: #555555;\">==<\/span> <span style=\"color: #ff6600;\">0<\/span> ){ <span style=\"color: #006699; font-weight: bold;\">return<\/span> a; }\r\n    <span style=\"color: #006699; font-weight: bold;\">else<\/span>{\r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> gcd(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n    }\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> main() {\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #ff6600;\">100<\/span>, <span style=\"color: #ff6600;\">33<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #ff6600;\">3.5<\/span>,<span style=\"color: #ff6600;\">4.0<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #cc3300;\">\"100\"<\/span>,<span style=\"color: #cc3300;\">\"10\"<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The error message is quite explicit.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6258\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcd2.png\" alt=\"gcd2\" width=\"650\" height=\"262\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcd2.png 1130w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcd2-300x121.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcd2-1024x413.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcd2-768x310.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>The compiler complains immediately that a <code>double<\/code> or a <code>const cha<\/code>r* is not an integral data type. Consequentially, the <code>static_assert<\/code> expression in (1) fired<\/p>\n<p>But correctness means that you can use the type-traits libraries to implement concepts such as <code>Integral<\/code>, <code>SignedIntegral<\/code>, and <code>UnsignedIntegral<\/code> in C++20.<\/p>\n<p><!-- HTML generated using hilite.me --><\/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;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>\r\nconcept Integral <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>is_integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value;                      <span style=\"color: #0099ff;\">\/\/ (1)<\/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>\r\nconcept SignedIntegral <span style=\"color: #555555;\">=<\/span> Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span> <span style=\"color: #555555;\">&amp;&amp;<\/span> std<span style=\"color: #555555;\">::<\/span>is_signed<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value;   <span style=\"color: #0099ff;\">\/\/ (2)<\/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>\r\nconcept UnsignedIntegral <span style=\"color: #555555;\">=<\/span> Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span> <span style=\"color: #555555;\">&amp;&amp;<\/span> <span style=\"color: #555555;\">!<\/span>SignedIntegral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span>;\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The concept <code>Integral<\/code> uses the type-traits functions directly <code>std::is_integral<\/code> (1) and the concept <code>SignedIntegral<\/code> the type-traits function<code> std::is_signed<\/code> (2).<\/p>\n<p>Let&#8217;s try it out and use the concept <code>Integral<\/code> directly.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/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;\">\/\/ gcdIntegral.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;type_traits&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>\r\nconcept Integral <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>is_integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value;\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>\r\nconcept SignedIntegral <span style=\"color: #555555;\">=<\/span> Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span> <span style=\"color: #555555;\">&amp;&amp;<\/span> std<span style=\"color: #555555;\">::<\/span>is_signed<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value;\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>\r\nconcept UnsignedIntegral <span style=\"color: #555555;\">=<\/span> Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span> <span style=\"color: #555555;\">&amp;&amp;<\/span> <span style=\"color: #555555;\">!<\/span>SignedIntegral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span>;\r\n\r\nIntegral <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">gcd<\/span>(Integral <span style=\"color: #006699; font-weight: bold;\">auto<\/span> a, decltype(a) b) {\r\n    <span style=\"color: #006699; font-weight: bold;\">if<\/span>( b <span style=\"color: #555555;\">==<\/span> <span style=\"color: #ff6600;\">0<\/span> ){ <span style=\"color: #006699; font-weight: bold;\">return<\/span> a; }\r\n    <span style=\"color: #006699; font-weight: bold;\">else<\/span>{\r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> gcd(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n    }\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> gcd(<span style=\"color: #ff6600;\">100<\/span>, <span style=\"color: #ff6600;\">33<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #ff6600;\">3.5<\/span>,<span style=\"color: #ff6600;\">4.0<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #cc3300;\">\"100\"<\/span>,<span style=\"color: #cc3300;\">\"10\"<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Now, the gcd algorithm is easier to read. It requires that the first argument <code>a<\/code> and its return type are integral data types. To ensure that the second argument <code>b<\/code> has the same type as the first type <code>a <\/code>I specified its type as <code>decltype(a)<\/code>. Consequentially, this implementation of the <code>gcd<\/code> algorithm and the previous one in<code> gcd2.cp<\/code>p are equivalent.<\/p>\n<p>Now, the error message is more verbose such as the previous one.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6259\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral.png\" alt=\"gcdIntegral\" width=\"650\" height=\"267\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral.png 1791w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral-300x123.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral-1024x421.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral-768x316.png 768w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegral-1536x631.png 1536w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>The error message of the GCC is not only too verbose but also too difficult to read. Let me try out Clang on the <a href=\"https:\/\/godbolt.org\/z\/1W5E1bb3r\">Compiler Explorer<\/a>. The error message about the erroneous usage of double reads like prose:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6260\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegralClang.png\" alt=\"gcdIntegralClang\" width=\"600\" height=\"185\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegralClang.png 884w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegralClang-300x92.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/12\/gcdIntegralClang-768x236.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>I don&#8217;t think an error message could be easier to read.<\/p>\n<p>Finally, I wanted to try out the latest Microsoft Visual Studio Compiler. This compiler supports concepts with one exception: the so-called abbreviated function template syntax. You may already guess it. I used the abbreviated function template syntax in my gcd algorithm. You can read more about this nice syntax in my previous post: <a href=\"https:\/\/www.modernescpp.com\/index.php\/c-20-concept-syntactic-sugar\">C++20: Concepts &#8211; Syntactic Sugar<\/a>.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>Of course, you know what I will write about in my next post: the performance story of the type-traits library.<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The two main goals of the type-traits library are compelling: correctness and optimization. Today, I write about correctness.<\/p>\n","protected":false},"author":21,"featured_media":6251,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[376],"tags":[419],"class_list":["post-6261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-templates","tag-type-traits"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6261","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=6261"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6261\/revisions"}],"predecessor-version":[{"id":6688,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6261\/revisions\/6688"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/6251"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=6261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}