{"id":5830,"date":"2019-12-20T07:31:58","date_gmt":"2019-12-20T07:31:58","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-20-concepts-what-we-dont-get\/"},"modified":"2023-06-26T09:56:32","modified_gmt":"2023-06-26T09:56:32","slug":"c-20-concepts-what-we-dont-get","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-20-concepts-what-we-dont-get\/","title":{"rendered":"C++20: Concepts &#8211; What we don&#8217;t get"},"content":{"rendered":"<p>The Template Introduction from the Concepts TS is a new way to use concepts. This syntactic variant is not included in the Concepts Draft and, therefore, in the C++20 standard. But I don&#8217;t know what the farther-away future brings.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<h3>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5808\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/11\/TimelineCpp20Concepts.png\" alt=\"TimelineCpp20Concepts\" width=\"600\" height=\"235\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/11\/TimelineCpp20Concepts.png 954w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/11\/TimelineCpp20Concepts-300x118.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/11\/TimelineCpp20Concepts-768x301.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/h3>\n<h3>Template Introduction<\/h3>\n<p>Let me first start with a short riddle. Which of the following syntactic variants is not possible with the draft of the concept?<\/p>\n<p>&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;\">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;\">\/\/ (1)<\/span>\r\nrequires Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span>\r\nT gcd(T a, T 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> <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #cc00ff;\">gcd<\/span>(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n}\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;\">\/\/ (2)<\/span>\r\nT gcd1(T a, T b) requires Integral<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/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> <span style=\"color: #006699; font-weight: bold;\">return<\/span> gcd(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n}\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">template<\/span><span style=\"color: #555555;\">&lt;<\/span>Integral T<span style=\"color: #555555;\">&gt;<\/span>                                  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\nT gcd2(T a, T 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> <span style=\"color: #006699; font-weight: bold;\">return<\/span> gcd(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n}\r\n\r\nIntegral <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gcd3(Integral <span style=\"color: #006699; font-weight: bold;\">auto<\/span> a, Integral <span style=\"color: #006699; font-weight: bold;\">auto<\/span> b){ <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/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> <span style=\"color: #006699; font-weight: bold;\">return<\/span> gcd(b, a <span style=\"color: #555555;\">%<\/span> b);\r\n}\r\n\r\nIntegral{T}                                           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (5)<\/span>\r\nIntegral gcd(T a, T 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<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Maybe, you don&#8217;t know. So let me name the five variants.<\/p>\n<ol>\n<li>Requires clause<\/li>\n<li>Trailing requires clause<\/li>\n<li>Constrained template parameters<\/li>\n<li>Abbreviated Function Templates<\/li>\n<li>Template Introduction<\/li>\n<\/ol>\n<p>I assume you have chosen the most obscure one: Template Introduction. You are right. Maybe we get it with a later C++ standard, but I&#8217;m highly skeptical. In my talks in the last year, I heard no one complaining that they are not part of C++20. I&#8217;m also not a big fan of Template Introduction because they introduced a new asymmetry; you know, I&#8217;m not a fan of asymmetries.<\/p>\n<\/p>\n<h3>The Asymmetry<\/h3>\n<p>Instead of declaring your constrained template using <span style=\"font-family: courier new,courier;\">template&lt;Integral T&gt;<\/span>, you can see <span style=\"font-family: courier new,courier;\">Integral{T}<\/span>. Here, you see the asymmetry. You can only use Template Introduction with concepts (constrained placeholders) but not with <span style=\"font-family: courier new, courier;\">auto<\/span> (unconstrained placeholders). The following example makes my point. This and the following example is based on the previous concepts&#8217; TS specification and compiled with the GCC.<\/p>\n<p>&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: #0099ff; font-style: italic;\">\/\/ templateIntroduction.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;type_traits&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&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 <span style=\"color: #007788; font-weight: bold;\">bool<\/span> Integral(){\r\n  <span style=\"color: #006699; font-weight: bold;\">return<\/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\r\nIntegral{T}                            <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\nIntegral gcd(T a, T 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\nIntegral{T}                            <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">ConstrainedClass<\/span>{};\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">\/*<\/span>\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">auto{T}                                \/\/ (4)<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">auto gcd(T a, T b){<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">  if( b == 0 ){ return a; }<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">  else{<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">    return gcd(b, a % b);<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">  }<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">}<\/span>\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">auto{T}                                \/\/ (5)<\/span>\r\n<span style=\"color: #0099ff; font-style: italic;\">class ConstrainedClass{};<\/span>\r\n\r\n<span style=\"color: #0099ff; font-style: italic;\">*\/<\/span>\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  <span style=\"color: #006699; font-weight: bold;\">auto<\/span> res<span style=\"color: #555555;\">=<\/span> gcd(<span style=\"color: #ff6600;\">100<\/span>, <span style=\"color: #ff6600;\">10<\/span>); \r\n\r\n  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> constrainedClass;\r\n  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">double<\/span><span style=\"color: #555555;\">&gt;<\/span> constrainedClass1;  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/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>I use Template introduction for the function template <span style=\"font-family: courier new,courier;\">gcd<\/span> (line 1) and the class template <span style=\"font-family: comic sans ms,sans-serif;\">ConstrainedClass<\/span> (line 2). As expected,&nbsp; the concept will kick in if I try to instantiate <span style=\"font-family: courier new,courier;\">ConstraintedClass<\/span> for <span style=\"font-family: courier new,courier;\">double<\/span> (line 3).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5216\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/03\/templateIntroductionError.png\" alt=\"templateIntroductionError\" width=\"600\" height=\"149\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/03\/templateIntroductionError.png 864w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/03\/templateIntroductionError-300x74.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/03\/templateIntroductionError-768x190.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>I don&#8217;t like it that I can not just replace <span style=\"font-family: courier new,courier;\">Integral<\/span> with <span style=\"font-family: courier new,courier;\">auto<\/span>, such as in lines 4 and 5. Up to this point, I used constrained placeholders (concepts) and unconstrained placeholders (auto) interchangeably in my posts to concepts. This straightforward principle is gone with Template Introduction.<\/p>\n<p>Of course, I could easily overcome this restriction by defining a concept that is always evaluated to <span style=\"font-family: courier new, courier;\">true<\/span>.<\/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;\">\/\/ templateIntroductionGeneric.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;string&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;typeinfo&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;utility&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> NoDefaultConstructor{               <span style=\"color: #0099ff; font-style: italic;\">\/\/ (5)<\/span>\r\n    NoDefaultConstructor() <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">delete<\/span>;\r\n};\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;\">\/\/ (1)<\/span>\r\nconcept <span style=\"color: #007788; font-weight: bold;\">bool<\/span> Generic(){\r\n  <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #336666;\">true<\/span>;\r\n}\r\n\r\nGeneric{T}                                 <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\nGeneric gcd(T a, T 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\nGeneric{T}                                 <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">ConstrainedClass<\/span>{\r\n<span style=\"color: #9999ff;\">public:<\/span>\r\n  ConstrainedClass(){\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #006699; font-weight: bold;\">typeid<\/span>(decltype(std<span style=\"color: #555555;\">::<\/span>declval<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;<\/span>())).name()   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\r\n              <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n  }\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> std<span style=\"color: #555555;\">::<\/span>endl;\r\n  \r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"gcd(100, 10): \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> gcd(<span style=\"color: #ff6600;\">100<\/span>, <span style=\"color: #ff6600;\">10<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\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  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> genericClassInt;\r\n  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>string<span style=\"color: #555555;\">&gt;<\/span> genericClassString;\r\n  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">double<\/span><span style=\"color: #555555;\">&gt;<\/span> genericClassDouble;\r\n  ConstrainedClass<span style=\"color: #555555;\">&lt;<\/span>NoDefaultConstructor<span style=\"color: #555555;\">&gt;<\/span> genericNoDefaultConstructor;\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><span style=\"font-family: courier new,courier;\">Generic<\/span> (line 1) is a concept that returns <span style=\"font-family: courier new,courier;\">true<\/span> for all types. Now, I can unify the syntax and define an unconstrained function template (line 4) and an unconstrained class template (line 3). The terms unconstrained or constrained function templates or class templates are not official. I coined them for simplicity reasons.<\/p>\n<p>The expression<span style=\"font-family: courier new,courier;\"> typeid(decltype(std::declval&lt;T&gt;())).name()<\/span> (line 4)&nbsp; may look weird to you. <span style=\"font-family: courier new, courier;\">std::declvar&lt;T&gt; <\/span>(C++11) converts the type parameter T into a reference type. Thanks to the reference type, you can use it in a <span style=\"font-family: courier new, courier;\">decltype<\/span> expression to invoke any member function on<span style=\"font-family: courier new, courier;\"> T<\/span> without constructing <span style=\"font-family: courier new, courier;\">T<\/span>. <span style=\"font-family: courier new, courier;\"><\/span>It works even for a type T without a default constructor (line 5). In my case (line 4), I invoked the constructor on the reference type to get the string representation of the type parameter T. Here is the program&#8217;s output with GCC.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5829\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/12\/templateIntroductionGeneric2.png\" alt=\"templateIntroductionGeneric2\" width=\"500\" height=\"232\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/12\/templateIntroductionGeneric2.png 613w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2019\/12\/templateIntroductionGeneric2-300x139.png 300w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n<h3>What&#8217;s next?<\/h3>\n<p>One big topic is left to complete the story concepts: define your concept. Most of the time, you reinvent the wheel because C++20 has many pre-defined concepts. Anyway, I will present in my <a href=\"https:\/\/www.modernescpp.com\/index.php\/c-20-concepts-predefined-concepts\">next post <\/a>the pre-defined concepts and how you can define your own.<\/p>\n<p>&nbsp;<\/p>\n<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Template Introduction from the Concepts TS is a new way to use concepts. This syntactic variant is not included in the Concepts Draft and, therefore, in the C++20 standard. But I don&#8217;t know what the farther-away future brings.<\/p>\n","protected":false},"author":21,"featured_media":5808,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[415],"class_list":["post-5830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-concepts"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5830","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=5830"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5830\/revisions"}],"predecessor-version":[{"id":6761,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5830\/revisions\/6761"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5808"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}