{"id":20745,"date":"2018-09-04T09:12:09","date_gmt":"2018-09-04T16:12:09","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/?p=20745"},"modified":"2022-06-23T18:56:25","modified_gmt":"2022-06-23T18:56:25","slug":"stdoptional-how-when-and-why","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/stdoptional-how-when-and-why\/","title":{"rendered":"std::optional: How, when, and why"},"content":{"rendered":"<p><i>This post is part of a regular series of posts where the C++ product team here at Microsoft and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc. Today\u2019s post is by Casey Carter.<\/i><\/p>\n<p>C++17\u00a0adds several new\u00a0&#8220;vocabulary types&#8221;\u00a0\u2013\u00a0types intended to be used in the interfaces between components from different sources \u2013 to the standard library.\u00a0<a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/msvc-the-best-choice-for-windows\/\">MSVC<\/a>\u00a0has been shipping implementations of <code>std::optional<\/code>,\u00a0<code>std::any<\/code>, and\u00a0<code>std::variant<\/code>\u00a0since the Visual Studio 2017 release, but we haven&#8217;t provided any guidelines on how and when these vocabulary types should be used. This article on\u00a0<code>std::optional<\/code>\u00a0is the first of a series that will examine each of the vocabulary types in turn.<\/p>\n<h3>The need for &#8220;sometimes-a-thing&#8221;<\/h3>\n<p>How do you write a function that optionally accepts or returns an object? The traditional solution is to choose one of the potential values as a sentinel to indicate the\u00a0<em>absence<\/em>\u00a0of a value:<\/p>\n<pre class=\"lang:default decode:true\">void maybe_take_an_int(int value = -1); \/\/ an argument of -1 means \"no value\"\r\nint maybe_return_an_int(); \/\/ a return value of -1 means \"no value\"\r\n<\/pre>\n<p>This works reasonably well when one of the representable values of the type never occurs in practice. It&#8217;s less great when there&#8217;s no obvious choice of sentinel and you want to be able to pass all representable values. If that&#8217;s the case, the typical approach is to use a separate\u00a0boolean\u00a0to indicate whether the optional parameter holds a valid value:<\/p>\n<pre class=\"lang:default decode:true\">void maybe_take_an_int(int value = -1, bool is_valid = false);\r\nvoid or_even_better(pair&lt;int,bool&gt; param = std::make_pair(-1, false));\r\npair&lt;int, bool&gt; maybe_return_an_int();\r\n<\/pre>\n<p>This is also feasible, but awkward. The &#8220;two distinct parameters&#8221; technique of\u00a0<code>maybe_take_an_int<\/code>\u00a0requires the caller to pass two things instead of one to represent a single notion, and fails silently when the caller forgets the\u00a0<code>bool<\/code>\u00a0and simply calls\u00a0<code>maybe_take_an_int(42)<\/code>. The use of\u00a0<code>pair<\/code>\u00a0in the other two functions avoids those problems, but it&#8217;s possible for the user of the\u00a0<code>pair<\/code>\u00a0to forget to check the\u00a0<code>bool<\/code>\u00a0and potentially use a garbage value in the\u00a0<code>int<\/code>. Passing\u00a0<code>std::make_pair(42, true)<\/code>\u00a0or\u00a0<code>std::make_pair(whatever, false)<\/code>\u00a0is also hugely different than passing\u00a0<code>42<\/code>\u00a0or nothing \u2013 we&#8217;ve made the interface hard to use.<\/p>\n<h3>The need for &#8220;not-yet-a-thing&#8221;<\/h3>\n<p>How do you write a class with a member object whose initialization is delayed, i.e., optionally contains an object? For whatever reason, you do not want to initialize this member in a constructor. The initialization may happen in a later mandatory call, or it may happen only on request. When the object is destroyed the member must be destroyed only if it has been initialized. It&#8217;s possible to achieve this by allocating raw storage for the member object, using a\u00a0<code>bool<\/code>\u00a0to track its initialization status, and doing horrible placement\u00a0<code>new<\/code>\u00a0tricks:<\/p>\n<pre class=\"lang:default decode:true\">using T = \/* some object type *\/;\r\n\r\nstruct S {\r\n  bool is_initialized = false;\r\n  alignas(T) unsigned char maybe_T[sizeof(T)];\r\n\r\n  void construct_the_T(int arg) {\r\n    assert(!is_initialized);\r\n    new (&amp;maybe_T) T(arg);\r\n    is_initialized = true;\r\n  }\r\n\r\n  T&amp; get_the_T() {\r\n    assert(is_initialized);\r\n    return reinterpret_cast&lt;T&amp;&gt;(maybe_T);\r\n  }\r\n\r\n  ~S() {\r\n    if (is_initialized) {\r\n      get_the_T().~T(); \/\/ destroy the T\r\n    }\r\n  }\r\n\r\n  \/\/ ... lots of code ...\r\n};\r\n<\/pre>\n<p>The <code>\"lots of code\"<\/code>\u00a0comment in the body of\u00a0<code>S<\/code>\u00a0is where you write copy\/move constructors\/assignment operators that do the right thing depending on whether the source and target objects contain an initialized\u00a0<code>T<\/code>. If this all seems horribly messy and fragile to you, then give yourself a pat on the back \u2013 your instincts are right. We&#8217;re walking right along the cliff&#8217;s edge where small mistakes will send us tumbling into undefined behavior.<\/p>\n<p>Another possible solution to many of the above problems is to dynamically allocate the &#8220;optional&#8221; value and pass it via pointer\u00a0\u2013\u00a0ideally\u00a0<code>std::unique_ptr<\/code>. Given that we C++ programmers are accustomed to using pointers, this solution has good usability: a\u00a0null\u00a0pointer indicates the no-value condition,\u00a0<code>*<\/code>\u00a0is used to access the value,\u00a0<code>std::make_unique&lt;int&gt;(42)<\/code>\u00a0is only slightly awkward compared to\u00a0<code>return 42<\/code>\u00a0and\u00a0<code>unique_ptr<\/code>\u00a0handles the deallocation for us automatically.\u00a0Of course\u00a0usability is not the only concern; readers accustomed to C++&#8217;s zero-overhead abstractions will immediately pounce upon this solution and complain that dynamic allocation is orders of magnitude more expensive than simply returning an integer. We&#8217;d like to solve this class of problem without\u00a0<em>requiring<\/em>\u00a0dynamic allocation.<\/p>\n<h3><code style=\"font-size: x-large;\">optional<\/code>\u00a0is\u00a0mandatory<\/h3>\n<p>C++17&#8217;s solution to the above problems is\u00a0<code>std::optional<\/code>.\u00a0<code>optional&lt;T&gt;<\/code>\u00a0directly addresses the issues that arise when passing or storing what may-or-may-not-currently-be an object.\u00a0<code>optional&lt;T&gt;<\/code>\u00a0provides interfaces to determine if it contains a\u00a0<code>T<\/code>\u00a0and to query the stored value. You can initialize an\u00a0<code>optional<\/code> with an actual\u00a0<code>T<\/code>\u00a0value, or default-initialize it (or initialize with\u00a0<code>std::nullopt<\/code>) to put it in the &#8220;empty&#8221; state.\u00a0<code>optional&lt;T&gt;<\/code>\u00a0even extends\u00a0<code>T<\/code>&#8216;s ordering operations\u00a0<code>&lt;<\/code>,\u00a0<code>&gt;<\/code>,\u00a0<code>&lt;=<\/code>,\u00a0<code>&gt;=<\/code>\u00a0\u2013 where an empty\u00a0<code>optional<\/code> compares as less than any\u00a0<code>optional<\/code>\u00a0that contains a\u00a0<code>T<\/code>\u00a0\u2013 so you can use it in some contexts exactly as if it were a\u00a0<code>T<\/code>.\u00a0<code>optional&lt;T&gt;<\/code>\u00a0stores the\u00a0<code>T<\/code>\u00a0object internally, so dynamic allocation is not necessary and in fact explicitly forbidden by the C++ Standard.<\/p>\n<p>Our functions that need to optionally pass a\u00a0<code>T<\/code>\u00a0would be declared as:<\/p>\n<pre class=\"lang:default decode:true\">void maybe_take_an_int(optional&lt;int&gt; potential_value = nullopt); \r\n  \/\/ or equivalently, \"potential_value = {}\"\r\noptional&lt;int&gt; maybe_return_an_int();\r\n<\/pre>\n<p>Since <code>optional&lt;T&gt;<\/code>\u00a0can be initialized from a\u00a0<code>T<\/code>\u00a0value, callers of\u00a0<code>maybe_take_an_int<\/code> need not change unless they were explicitly passing\u00a0<code>-1<\/code>\u00a0to indicate &#8220;not-a-value.&#8221; Similarly, the implementation of\u00a0<code>maybe_return_an_int<\/code> need only change places that are returning\u00a0<code>-1<\/code>\u00a0for &#8220;not-a-value&#8221; to instead return\u00a0<code>nullopt<\/code>\u00a0(or equivalently\u00a0<code>{}<\/code>).<\/p>\n<p>Callers of\u00a0<code>maybe_return_an_int<\/code>\u00a0and the implementation of\u00a0<code>maybe_take_an_int<\/code>\u00a0require more substantial changes. You can ask explicitly if an instance of\u00a0<code>optional<\/code>\u00a0holds a value using either the\u00a0<code>has_value<\/code>\u00a0member or by contextual conversion to\u00a0<code>bool<\/code>:<\/p>\n<pre class=\"lang:default decode:true\">optional&lt;int&gt; o = maybe_return_an_int();\r\nif (o.has_value()) { \/* ... *\/ }\r\nif (o) { \/* ... *\/ } \/\/ \"if\" converts its condition to bool\r\n<\/pre>\n<p>Once you know that the\u00a0<code>optional<\/code>\u00a0contains a value, you can extract it with the\u00a0<code>*<\/code>\u00a0operator:<\/p>\n<pre class=\"lang:default decode:true\">if (o) { cout &lt;&lt; \"The value is: \" &lt;&lt; *o &lt;&lt; '\\n'; }\r\n<\/pre>\n<p>or you can use the\u00a0value\u00a0member function to get the stored value or a\u00a0<code>bad_optional_access<\/code>\u00a0exception if there is none, and not bother with checking:<\/p>\n<pre class=\"lang:default decode:true\">cout &lt;&lt; \"The value is: \" &lt;&lt; o.value() &lt;&lt; '\\n';\r\n<\/pre>\n<p>or the\u00a0<code>value_or<\/code>\u00a0member function if you&#8217;d rather get a fallback value than an exception from an empty\u00a0<code>optional<\/code>:<\/p>\n<pre class=\"lang:default decode:true\">cout &lt;&lt; \"The value might be: \" &lt;&lt; o.value_or(42) &lt;&lt; '\\n';\r\n<\/pre>\n<p>All of which together means we cannot inadvertently use a garbage value as was the case for the &#8220;traditional&#8221; solutions. Attempting to access the contained value of an empty\u00a0<code>optional<\/code>\u00a0results in an exception if accessed with the\u00a0<code>value()<\/code>\u00a0member, or undefined behavior if accessed via the\u00a0<code>*<\/code>\u00a0operator that can be caught by debug libraries and static analysis tools. Updating the &#8220;old&#8221; code is probably as simple as replacing validity tests like\u00a0<code>value ==\u00a0not_a_value_sentinel<\/code>\u00a0and\u00a0<code>if (is_valid)<\/code>\u00a0with\u00a0<code>opt_value.has_value()<\/code>\u00a0and\u00a0<code>if (opt_value)<\/code>\u00a0and replacing uses with\u00a0<code>*opt_value<\/code>.<\/p>\n<p>Returning to the concrete example, your function that looks up a string given an integer can simply return\u00a0<code>optional&lt;string&gt;<\/code>. This avoids the problems of the suggested solutions; we can<\/p>\n<ul>\n<li>easily discern the no-value case from the value-found case, unlike for the &#8220;return a default value&#8221; solution,<\/li>\n<li>report the no-value case without using exception handling machinery, which is likely too expensive if such cases are frequent rather than exceptional,<\/li>\n<li>avoid leaking implementation details to the caller as would be necessary to expose an &#8220;end&#8221; iterator with which they could compare a returned iterator.<\/li>\n<\/ul>\n<p>Solving the delayed initialization problem is straightforward: we simply add an\u00a0<code>optional&lt;T&gt;<\/code>\u00a0member to our class. The standard library implementer is responsible for getting the placement <code>new<\/code> handling correct, and\u00a0<code>std::optional<\/code>\u00a0already handles all of the special cases for the copy\/move constructors\/assignment operators:<\/p>\n<pre class=\"lang:default decode:true\">using T = \/* some object type *\/;\r\n\r\nstruct S {\r\n  optional&lt;T&gt; maybe_T;    \r\n\r\n  void construct_the_T(int arg) {\r\n    \/\/ We need not guard against repeat initialization;\r\n    \/\/ optional's emplace member will destroy any \r\n    \/\/ contained object and make a fresh one.        \r\n    maybe_T.emplace(arg);\r\n  }\r\n\r\n  T&amp; get_the_T() { \r\n    assert(maybe_T);\r\n    return *maybe_T;    \r\n    \/\/ Or, if we prefer an exception when maybe_T is not initialized:\r\n    \/\/ return maybe_T.value();\r\n  }\r\n\r\n  \/\/ ... No error-prone handwritten special member functions! ...\r\n};\r\n<\/pre>\n<p><code>optional<\/code>\u00a0is particularly well-suited to the delayed initialization problem because it is itself an instance of delayed initialization. The contained\u00a0<code>T<\/code>\u00a0may be initialized at construction, or sometime later, or never. Any contained\u00a0<code>T<\/code>\u00a0must be destroyed when the\u00a0<code>optional<\/code>\u00a0is destroyed. The designers of\u00a0<code>optional<\/code>\u00a0have already answered most of the questions that arise in this context.<\/p>\n<h3>Conclusions<\/h3>\n<p>Any time you need a tool to express &#8220;value-or-not-value&#8221;, or &#8220;possibly an answer&#8221;, or &#8220;object with delayed\u00a0initialization&#8221;, you should reach into your toolbox for\u00a0<code>std::optional<\/code>. Using a vocabulary type for these cases raises the level of abstraction, making it easier for others to understand what your code is doing. The declarations\u00a0<code>optional&lt;T&gt;\u00a0f();<\/code>\u00a0and\u00a0<code>void g(optional&lt;T&gt;);<\/code>\u00a0express intent more clearly and concisely than do\u00a0<code>pair&lt;T, bool&gt; f();<\/code>\u00a0or\u00a0<code>void g(T\u00a0t, bool\u00a0is_valid);<\/code>. Just as is the case with words, adding to our vocabulary of types increases our capacity to describe complex problems simply\u00a0\u2013\u00a0it makes us more efficient.<\/p>\n<p>If you have any questions, please feel free\u00a0to post in the comments below.\u00a0You can also send\u00a0any\u00a0comments and suggestions directly to the author via e-mail at\u00a0<a href=\"mailto:cacarter@microsoft.com\">cacarter@microsoft.com<\/a>, or Twitter @CoderCasey. Thank you!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is part of a regular series of posts where the C++ product team here at Microsoft and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc. Today\u2019s post is by Casey [&hellip;]<\/p>\n","protected":false},"author":1529,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[277],"tags":[],"class_list":["post-20745","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-writing-code"],"acf":[],"blog_post_summary":"<p>This post is part of a regular series of posts where the C++ product team here at Microsoft and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc. Today\u2019s post is by Casey [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/20745","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/1529"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=20745"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/20745\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=20745"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=20745"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=20745"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}