{"id":108509,"date":"2023-08-01T07:00:00","date_gmt":"2023-08-01T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108509"},"modified":"2023-08-01T10:04:25","modified_gmt":"2023-08-01T17:04:25","slug":"20230801-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230801-00\/?p=108509","title":{"rendered":"Inside STL: The pair and the compressed pair"},"content":{"rendered":"<p>The C++ language comes with a standard library. When you&#8217;re debugging your C++ code, you may have to go digging inside the implementation to extract information from crash dumps. This mini-series is going to look at how various C++ standard library types are implemented by the three major implementations (clang, gcc, and msvc).<\/p>\n<p>We&#8217;ll start with the lowly <code>std::<wbr \/>pair<\/code>. Its definition is quite simple.<\/p>\n<pre>template&lt;typename T1, typename T2&gt;\r\nstruct pair\r\n{\r\n    T1 first;\r\n    T2 second;\r\n};\r\n<\/pre>\n<p>The names of the members of <code>std::<wbr \/>pair<\/code> are required by the C++ language standard, so you don&#8217;t see any variation here. Here&#8217;s how it looks in the Windows debugger:<\/p>\n<pre style=\"white-space: pre-wrap;\">0:000&gt; ?? t\r\nstruct std::pair&lt;int,int&gt;\r\n   +0x000 first            : 0n42\r\n   +0x004 second           : 0n99\r\n<\/pre>\n<p>Compressed pairs are a fancy kind of pair that are used internally by C++ library implementations. Empty classes are often used in the C++ standard library: Allocators, hash objects, comparison objects. The C++ language requires that even empty classes have a nonzero size if they are standalone objects or members of other classes.<\/p>\n<p>Rather than wasting a byte per object to hold these empty classes, the standard library implementation uses a trick: The C++ language does not require empty <i>base classes<\/i> to have a nonzero size, provided the base class&#8217;s type does not match that of the first member of the class.\u00b9 Treating empty base classes as having zero size is known as the <i>empty base optimization<\/i> or EBO.<\/p>\n<p>Okay, so suppose you want a class that acts like a <code>std::pair&lt;T1, T2&gt;<\/code> where <code>T1<\/code> is an empty class. Instead of the na\u00efve definition<\/p>\n<pre>template&lt;typename T1, typename T2&gt;\r\nstruct pair\r\n{\r\n    T1 first;\r\n    T2 second;\r\n};\r\n<\/pre>\n<p>you could use this:<\/p>\n<pre>template&lt;typename T1, typename T2&gt;\r\nstruct fancy_pair_t1 : T1\r\n{\r\n    T2 second;\r\n};\r\n<\/pre>\n<p>If <code>T1<\/code> is an empty class, then using it as a base class allows the compiler to shrink it down to zero bytes. Similarly, if you suspect the second type of being an empty class, you can derive from <code>T2<\/code> and keep <code>T1<\/code> as a member.<\/p>\n<p>Inside the standard library implementation, there&#8217;s code that determines at compile time whether to use <code>fancy_<wbr \/>pair_<wbr \/>t1<\/code>, <code>fancy_<wbr \/>pair_<wbr \/>t2<\/code>, or a traditional <code>pair<\/code>.<\/p>\n<p>Just a reminder that the compressed pair is not part of the official C++ language standard library. It&#8217;s a helper class that is used internally by standard library implementations to squeeze out wasted bytes.<\/p>\n<p><b>Bonus chatter<\/b>: The Boost library comes with an implementation of <code>compressed_pair<\/code>.<\/p>\n<p><b>Bonus bonus chatter<\/b>: The introduction of the <code>[[no_unique_address]]<\/code> attribute in C++20 avoids the need for compressed pairs in most if not all cases. You can get the same effect as an old-style compressed pair with the much simpler<\/p>\n<pre>template&lt;typename T1, typename T2&gt;\r\nstruct compressed_pair\r\n{\r\n    [[no_unique_address]] T1 first;\r\n    [[no_unique_address]] T2 second;\r\n};\r\n<\/pre>\n<p>Nevertheless, C++ standard library implementations continue to use their internal compressed pair types. There are a variety of reasons for this.<\/p>\n<ul>\n<li>It allows the same header to be used by both C++17 and C++20 clients.<\/li>\n<li>It preserves ABI compatibility.<\/li>\n<li>There is a general reluctance to make changes to working code without a concrete benefit.<\/li>\n<\/ul>\n<p>\u00b9 If the first member of the class has the same type as the base class, then you would have the problem of two separate objects of the same type at the same address.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Two fields in one object, how hard can it be?<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108509","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Two fields in one object, how hard can it be?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108509","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108509"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108509\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108509"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108509"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108509"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}