{"id":110521,"date":"2024-11-14T07:00:00","date_gmt":"2024-11-14T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110521"},"modified":"2024-11-22T07:05:27","modified_gmt":"2024-11-22T15:05:27","slug":"20241114-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241114-00\/?p=110521","title":{"rendered":"Solving the puzzle of trying to put an object into a <CODE>std::optional<\/CODE>"},"content":{"rendered":"<p>Last time, <a title=\"The puzzle of trying to put an object into a std::optional\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241113-00\/?p=110516\"> we investigated the puzzle of why the compiler wouldn&#8217;t let us put an object into a <code>std::optional<\/code><\/a>. It came down to the fact that the object is not copy-constructible, move-constructible, copy-assignable, or move-assignable, so there&#8217;s no way to put the temporary object into the <code>std::optional<\/code>.<\/p>\n<p>What we have to do is <i>construct<\/i> the object in place inside the <code>std::optional<\/code>. And the C++ standard library term for &#8220;construct an object inside a container&#8221; is &#8220;emplace&#8221;.<\/p>\n<pre>struct Doodad\r\n{\r\n    Doodad();\r\n    ~Doodad();\r\n    std::unique_ptr&lt;DoodadStuff&gt; m_stuff;\r\n};\r\n\r\nstruct Widget\r\n{\r\n    std::optional&lt;Doodad&gt; m_doodad;\r\n\r\n    Widget()\r\n    {\r\n        if (doodads_enabled()) {\r\n            <span style=\"border: solid 1px currentcolor;\">m_doodad.emplace();<\/span>\r\n        }\r\n    }\r\n};\r\n<\/pre>\n<p>The parameters to <code>emplace<\/code> are whatever parameters you would have passed to the <code>Doodad<\/code> constructor. In our case, we wanted the default constructor, so that means that we pass nothing to <code>emplace()<\/code>.<\/p>\n<p>I may as well take this time to review the various options for placing a value into a <code>std::optional<\/code>, because they are subtly different. For the purpose of this discussion, the <code>T<\/code> object being held inside a <code>std::optional&lt;T&gt;<\/code> will be called its &#8220;value&#8221;.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"2\">\u00a0<\/th>\n<th colspan=\"2\">Previous state of <tt>optional&lt;T&gt;<\/tt><\/th>\n<\/tr>\n<tr>\n<th>Empty<\/th>\n<th>Not empty<\/th>\n<\/tr>\n<tr>\n<td><tt>o.reset();<\/tt><\/td>\n<td>Nothing happens<\/td>\n<td>Destruct the existing <tt>T<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>o.emplace(args...);<\/tt><\/td>\n<td>Construct a <tt>T<\/tt> from <tt>args...<\/tt><\/td>\n<td>Destruct the existing <tt>T<\/tt> and<br \/>\nconstruct a new <tt>T<\/tt> from <tt>args...<\/tt><br \/>\nsame as <tt>o.reset(); o.emplace(args...);<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>o = v;<\/tt><\/td>\n<td>Construct a <tt>T<\/tt> from <tt>v<\/tt><br \/>\nsame as <tt>o.emplace(v)<\/tt><\/td>\n<td>Assign <tt>v<\/tt> to the existing value<br \/>\nsame as <tt>*o = v;<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>o = std::nullopt;<\/tt><\/td>\n<td>Nothing happens<\/td>\n<td>Destruct the existing <tt>T<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that the <code>o = v;<\/code> might construct the object, or it might assign the object, depending on the prior state of the <code>std::optional<\/code>. That&#8217;s why the requirements for the assignment operator require both constructibility and assignability from the right hand side. If you already know whether the object is empty or nonempty, you avoid the compiler having to generate code for both possibilities by going straight to <code>emplace()<\/code> method (if you know that it is empty), or going straight to <code>T<\/code>&#8216;s assignment operator <code>*o = v;<\/code> (if you know that it is nonempty). Note, though, that the penalty for guessing wrong varies depending on the path you take.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"2\">\u00a0<\/th>\n<th colspan=\"2\">Previous state of <tt>optional&lt;T&gt;<\/tt><\/th>\n<\/tr>\n<tr>\n<th>Empty<\/th>\n<th>Not empty<\/th>\n<\/tr>\n<tr>\n<td><tt>o.emplace(v);<\/tt><\/td>\n<td>Construct a <tt>T<\/tt> from <tt>args...<\/tt><\/td>\n<td style=\"border: double 1px currentcolor;\">Destruct the existing <tt>T<\/tt> and<br \/>\nconstruct a new <tt>T<\/tt> from <tt>v...<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>o.value() = v;<\/tt><\/td>\n<td><tt>std::bad_optional_access<\/tt> exception<\/td>\n<td>Assign <tt>v<\/tt> to the existing value<\/td>\n<\/tr>\n<tr>\n<td><tt>*o = v;<\/tt><\/td>\n<td>Undefined behavior<\/td>\n<td>Assign <tt>v<\/tt> to the existing value<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you try to emplace thinking that the optional is empty, but it is in fact nonempty, then instead of assigning the value, you destruct the old value and then construct a new one. This is a subtle difference, but it is significant because it runs the object&#8217;s destructor and then re-runs the object&#8217;s constructor.<\/p>\n<p>On the other hand, if you use <code>o.value() = v;<\/code> to assign a value when the optional is empty, you get a runtime exception. And even worse, if you use <code>*o = v;<\/code> to assign a value when the optional is empty, you get undefined behavior, and that&#8217;s super-bad.<\/p>\n<p>But wait, what if your type is not copyable, movable, <i>or<\/i> constructible? For example, maybe instead of a constructor, it has a factory method. How can you put one of these objects into a <code>std::<wbr \/>optional<\/code>? We&#8217;ll look at that next time.<\/p>\n<p><b>Bonus chatter<\/b>: In the case where you <code>emplace()<\/code> into a nonempty optional, the old value is destructed, and the new value is constructed. If the construction of the new value throws an exception, then the optional stays empty. This is another subtle difference from using the assignment operator, because a failed assignment does <i>not<\/i> destruct the optional&#8217;s value.<\/p>\n<p><b>Bonus bonus chatter<\/b>: You might think you can move a value out of an optional by doing<\/p>\n<pre>auto v = std::move(v.value());\r\n<\/pre>\n<p>but while this does move the value&#8217;s contents, the optional remains nonempty, with a moved-from value inside it. Even move-constructing an optional from an optional <i>does not empty the source<\/i>.<\/p>\n<pre>std::optional&lt;T&gt; p = std::move(o);\r\n\/\/ o is nonempty and contains a moved-from value\r\n<\/pre>\n<p>If you want to empty the optional, you can exchange it.<\/p>\n<pre>std::optional&lt;T&gt; p = std::exchange(o, std::nullopt);\r\n<\/pre>\n<p>You can equivalently write<\/p>\n<pre>std::optional&lt;T&gt; p = std::exchange(o, {});\r\n<\/pre>\n<p>but for some reason, msvc and gcc fail to optimize out the temporary empty <code>std::optional&lt;T&gt;{}<\/code>, so stick with <code>std::nullopt<\/code>.<\/p>\n<p><b>Bonus bonus bonus chatter<\/b>: If you want to construct a <code>std::optional<\/code> with an object already inside it, you can use <code>in_place<\/code> with constructor arguments.<\/p>\n<pre>\/\/ constructs the Doodad as if by Doodad(x, y, z)\r\nstd::optional&lt;Doodad&gt; o(std::in_place, x, y, z);\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>How do I set a value? Let me count the ways.<\/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-110521","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>How do I set a value? Let me count the ways.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110521","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=110521"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110521\/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=110521"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110521"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110521"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}