{"id":6358,"date":"2022-05-12T06:40:20","date_gmt":"2022-05-12T06:40:20","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/check-types-with-concepts\/"},"modified":"2023-06-26T09:06:20","modified_gmt":"2023-06-26T09:06:20","slug":"check-types-with-concepts","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/check-types-with-concepts\/","title":{"rendered":"Check Types with Concepts"},"content":{"rendered":"<p>Concepts are a powerful and elegant tool to check at compile time if a type fulfills. Thanks to<code> static_assert<\/code>, you can use concepts as a standalone feature: <code>static_assert(Concept&lt;T&gt;)<\/code>.<\/p>\n<p><!--more--><\/p>\n<p>&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=\"650\" height=\"255\" 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: 650px) 100vw, 650px\" \/><\/p>\n<p>I often have the question in my C++ class: How can I be sure that my data type is moveable? Well, you can either study the dependencies between the Big Six or define and use the concept Big Six. In my last post, &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/test-types-with-concepts\">Check Types with Concepts &#8211; The Motivation<\/a>&#8220;, I presented the first part of the answer and explained the very sophisticated dependencies between the Big Six. As a reminder, here are the Big Six, including move semantics:<\/p>\n<ul>\n<li>Default constructor:<code> X()<\/code><\/li>\n<li>Copy constructor: <code>X(const X&amp;)<\/code><\/li>\n<li>Copy assignment: <code>operator = (const X&amp;)<\/code><\/li>\n<li>Move constructor: <code>X(X&amp;&amp;)<\/code><\/li>\n<li>Move assignment:<code> operator = (X&amp;&amp;)<\/code><\/li>\n<li>Destructor:<code> ~(X)<\/code><\/li>\n<\/ul>\n<p>Today, I want to define and use the concept Big Six.<\/p>\n<p>Before I do that, I have a short disclaimer: C++20 already supports the concepts <code>std::semiregular<\/code> and<code> std::regular<\/code>.<\/p>\n<h2><code>std::semiregular<\/code> and <code>std::regular<\/code><\/h2>\n<p>A semiregular type has to support the Big Six and must be swappable:<\/p>\n<ul>\n<li>Default constructor:<code> X()<\/code><\/li>\n<li>Copy constructor: <code>X(const X&amp;)<\/code><\/li>\n<li>Copy assignment: <code>operator = (const X&amp;)<\/code><\/li>\n<li>Move constructor: <code>X(X&amp;&amp;)<\/code><\/li>\n<li>Move assignment:<code> operator = (X&amp;&amp;)<\/code><\/li>\n<li>Destructor:<code> ~(X)<\/code><\/li>\n<li>Swappable:<code> swap(X&amp;, X&amp;)<\/code><\/li>\n<\/ul>\n<p>Additionally,<code> std::regular<\/code> requires for a type<code> X<\/code> that it supports the concept <code>std::semiregular<\/code> and is equality comparable.<\/p>\n<ul>\n<li>Default constructor:<code> X()<\/code><\/li>\n<li>Copy constructor: <code>X(const X&amp;)<\/code><\/li>\n<li>Copy assignment: <code>operator = (const X&amp;)<\/code><\/li>\n<li>Move constructor: <code>X(X&amp;&amp;)<\/code><\/li>\n<li>Move assignment:<code> operator = (X&amp;&amp;)<\/code><\/li>\n<li>Destructor:<code> ~(X)<\/code><\/li>\n<li>Swappable:<code> swap(X&amp;, Y&amp;)<\/code><\/li>\n<li>Equality comparable:<code> bool operator == (const X&amp;, const X&amp;)<\/code><\/li>\n<\/ul>\n<p>That said, there is essentially no reason to define the concept BigSix. Just use the concept <code>std::semiregular,<\/code> because you get the swappable property for free. Here is a C++11 implementation of <code>std::swap<\/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%;\"><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\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));  \/\/ move constructor\r\n    a <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>move(b);     \/\/ move assignment\r\n    b <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>move(tmp);   \/\/ move assignment\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>When you invoke<code> swap(a, b)<\/code>, the compiler applies move semantics to its arguments <code>a<\/code> and <code>b<\/code>. Consequentially, a type supporting the concept BigSix also supports swappable and, therefore, supports the concept <code>std::semiregular<\/code>.<\/p>\n<p>Now, let me implement the concept BigSix.<\/p>\n<\/p>\n<h2>The Concept BigSix<\/h2>\n<p>Thanks to the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/header\/type_traits\">type traits functions<\/a>, implementing BigSix is a no-brainer. In the first step, I define the type traits <code>isBigSix<\/code> , and in the second step, I use it directly to define the concept <code>BigSix<\/code>. Here we are:<\/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;\">\/\/ bigSixConcept.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;algorithm&gt;<\/span>\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\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> isBigSix<span style=\"color: #555555;\">:<\/span> std<span style=\"color: #555555;\">::<\/span>integral_constant<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">bool<\/span>,\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_default_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_copy_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_copy_assignable<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_move_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_move_assignable<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_destructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value<span style=\"color: #555555;\">&gt;<\/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>\r\nconcept BigSix <span style=\"color: #555555;\">=<\/span> isBigSix<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>BigSix T<span style=\"color: #555555;\">&gt;<\/span>                                   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/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;\">struct<\/span> MyData{                                        <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n  MyData() <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n  MyData(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyData<span style=\"color: #555555;\">&amp;<\/span> ) <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n  MyData<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> MyData<span style=\"color: #555555;\">&amp;<\/span> m) <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\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> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n    MyData a, b;\r\n    swap(a, b);                                             <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n\r\n    static_assert(BigSix<span style=\"color: #555555;\">&lt;<\/span>MyData<span style=\"color: #555555;\">&gt;<\/span>, <span style=\"color: #cc3300;\">\"BigSix not supported\"<\/span>);  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <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, my function <code>swap<\/code> requires that the type parameter T supports BigSix (line 1). In line 3, I invoke the function <code>swap<\/code> with arguments of type <code>MyData<\/code>. Additionally, I explicitly check in line 4 if <code>MyData<\/code> supports the concept <code>BigSix<\/code>.<code> MyData<\/code> (line 2) has a default constructor and supports copy semantics. The program can be compiled and executed.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6356\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConcept.png\" alt=\"BigSixConcept\" width=\"600\" height=\"176\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConcept.png 721w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConcept-300x88.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Does this mean that <code>MyData <\/code>supports the concept <code>BigSix<\/code> and is, therefore, moved inside my function <code>swap<\/code>? Yes, <code>MyData<\/code> supports the concept BigSix, but no, <code>MyData<\/code> is not moved inside my function <code>swap<\/code>. Copy semantic kicks in as a fallback for move semantics.<\/p>\n<p>Here is a slightly modified program.<\/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;\">\/\/ bigSixConceptComments.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;algorithm&gt;<\/span>\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\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> isBigSix<span style=\"color: #555555;\">:<\/span> std<span style=\"color: #555555;\">::<\/span>integral_constant<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">bool<\/span>,\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_default_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_copy_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_copy_assignable<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_move_constructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_move_assignable<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value <span style=\"color: #555555;\">&amp;&amp;<\/span>\r\n                                      std<span style=\"color: #555555;\">::<\/span>is_destructible<span style=\"color: #555555;\">&lt;<\/span>T<span style=\"color: #555555;\">&gt;::<\/span>value<span style=\"color: #555555;\">&gt;<\/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>\r\nconcept BigSix <span style=\"color: #555555;\">=<\/span> isBigSix<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>BigSix T<span style=\"color: #555555;\">&gt;<\/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;\">struct<\/span> MyData{                                        \r\n    MyData() <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n    MyData(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyData<span style=\"color: #555555;\">&amp;<\/span> ) {\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: #cc3300; font-weight: bold;\">\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>;\r\n    }\r\n    MyData<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> MyData<span style=\"color: #555555;\">&amp;<\/span> m) {\r\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"copy assignment operator<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>;\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};\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> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n    MyData a, b;\r\n    swap(a, b);       \r\n    \r\n    static_assert(BigSix<span style=\"color: #555555;\">&lt;<\/span>MyData<span style=\"color: #555555;\">&gt;<\/span>, <span style=\"color: #cc3300;\">\"BigSix not supported\"<\/span>);                             \r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <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>I added comments to the copy constructor and copy assignment operator of <code>MyData<\/code>. Executing the program shows that both special member functions are used:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6357\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConceptComments.png\" alt=\"BigSixConceptComments\" width=\"400\" height=\"209\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConceptComments.png 493w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2022\/05\/BigSixConceptComments-300x157.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>By the way, this observation is already documented in cppreference.com. For example, a note about the type trait <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/types\/is_move_constructible\">std::is_move_constructible <\/a>states: &#8220;<em>Types without a move constructor, but with a copy constructor that accepts const T&amp; arguments, satisfy std::is_move_constructible.<\/em>&#8220;<\/p>\n<p>Okay, we are back to square one. We can decide if a type supports<code> BigSix<\/code>, but we cannot decide if a type is moved. If you want to know if your type supports move semantics and not that copy semantics is used as a fallback for move semantics, you must study the dependency table of my previous post: &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/test-types-with-concepts\">Check Types with Concepts &#8211; The Motivation<\/a>&#8220;.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>In my next post, I want to continue my story with ranges. Additionally, ranges will get many improvements in C++23.<\/p>\n<p>&nbsp;<\/p>\n<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Concepts are a powerful and elegant tool to check at compile time if a type fulfills. Thanks to static_assert, you can use concepts as a standalone feature: static_assert(Concept&lt;T&gt;).<\/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,416],"class_list":["post-6358","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-concepts","tag-regular"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6358","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=6358"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6358\/revisions"}],"predecessor-version":[{"id":6669,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6358\/revisions\/6669"}],"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=6358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}