{"id":103349,"date":"2020-01-20T07:00:00","date_gmt":"2020-01-20T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103349"},"modified":"2020-01-19T16:47:04","modified_gmt":"2020-01-20T00:47:04","slug":"20200120-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200120-00\/?p=103349","title":{"rendered":"Can shrinking a <CODE>std::string<\/CODE> throw an exception?"},"content":{"rendered":"<p>I had a C++ string that I wanted to truncate. Say, something like this:<\/p>\n<pre>void remove_extension(std::string&amp; s)\r\n{\r\n auto pos = s.rfind('.');\r\n if (pos != std::string::npos) {\r\n  s.resize(pos);\r\n }\r\n}\r\n<\/pre>\n<p>The question is whether this function can throw an exception. Can the call to <code>resize<\/code> throw an exception when used to make a string smaller?<\/p>\n<p>And the answer appears to be <i>yes<\/i>, at least in C++17.<\/p>\n<p>The specification of the <code>resize(n)<\/code> method in C++17 says that in the case where <code>n &lt;= size()<\/code>, &#8220;the function replaces the string designated by <code>*this<\/code> with a string of length <code>n<\/code> whose elements are a copy of the initial elements of the original string designated by <code>*this<\/code>.&#8221;<\/p>\n<p>In other words, the <code>resize(n)<\/code> method, when shrinking a string (or leaving the size unchanged), behaves as if a new string is created, which replaces the current string. And creating a new string may throw <code>bad_alloc<\/code>.<\/p>\n<p>Of course, implementations may use the <i>as-if<\/i> rule and resize the string in place, but the standard does not require them to do so.<\/p>\n<p>But wait, all is not lost. Because another way to shrink a string is to use the <code>erase(n)<\/code> method.<\/p>\n<ul>\n<li><b>[basic.string]<\/b>: <code>basic_string<\/code> is a contiguous container.<\/li>\n<li><b>[container.requirements.general]<\/b> (11): Unless otherwise specified\u2026, all container types defined in this Clause meet the following additional requirements:<\/li>\n<li><b>[container.requirements.general]<\/b> (11.3): No <code>erase()<\/code>\u2026 function throws an exception.<\/li>\n<li><b>[string.erase]<\/b>: <i>Throws<\/i>: <code>length_error<\/code> if <code>n &gt; max_size()<\/code>.<\/li>\n<\/ul>\n<p>There are a few things referenced in the &#8220;\u2026&#8221; portion of <b>[container.requirements.general]<\/b> (11), but they do not apply to <code>basic_string<\/code>.<\/p>\n<p>Hooray, we can use the <code>erase<\/code> method to shrink the string and avoid an exception.<\/p>\n<pre>void remove_extension(std::string&amp; s)\r\n{\r\n auto pos = s.rfind('.');\r\n if (pos != std::string::npos) {\r\n  s.<span style=\"color: blue;\">erase<\/span>(pos);\r\n }\r\n}\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: It appears that the issue of <code>resize()<\/code> throwing an exception when trimming was brought up\u00b9 by <a href=\"https:\/\/twitter.com\/StephanTLavavej\"> Stephan T. Lavavej<\/a> and fixed by <a href=\"https:\/\/github.com\/timsong-cpp\"> Tim Song<\/a> in <a href=\"https:\/\/wg21.link\/P1148\"> P1148R0<\/a>: Starting in C++20, if you call the <code>resize()<\/code> method to shrink the string (or keep it the same size), the behavior is defined in terms of erasure and therefore does not throw an exception.<\/p>\n<p>\u00b9 I could have written &#8220;raised&#8221; but I didn&#8217;t.\u00b2<\/p>\n<p>\u00b2 Except that I just did.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It depends on how you shrink it.<\/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-103349","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It depends on how you shrink it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103349","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=103349"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103349\/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=103349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103349"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}