{"id":105454,"date":"2021-07-19T07:00:00","date_gmt":"2021-07-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105454"},"modified":"2021-07-19T08:13:56","modified_gmt":"2021-07-19T15:13:56","slug":"20210719-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210719-00\/?p=105454","title":{"rendered":"C++11 braced initialization made the impossible possible (and how to fix it so it stays impossible)"},"content":{"rendered":"<p>Suppose you have a private nested type. You might use this because you need your constructor to be public in order to work with some framework,\u00b9 but you don&#8217;t want people to do their own <code>make_<\/code><code>unique<\/code>; you want them to go through your factory.<\/p>\n<pre>class Package\r\n{\r\n  struct private_constructor { };\r\n\r\npublic:\r\n  \/\/ Do not call constructor directly. Use CreatePackage instead.\r\n  Package(int id, private_constructor);\r\n\r\n  static Package CreatePackage(int id, int flavor)\r\n  {\r\n    Package package(id, private_constructor());\r\n    ... do other stuff that gets the package ready ...\r\n    return package;\r\n  }\r\n};\r\n\r\nvoid bad_boy()\r\n{\r\n  \/\/ This doesn't work. Wrong number of parameters.\r\n  Package package(3);\r\n\r\n  \/\/ This doesn't work. private_constructor is a private type.\r\n  Package package(3, Package::private_constructor());\r\n}\r\n<\/pre>\n<p>But C++11 introduced braced initialization, and the bad boy can use that to construct the type without naming it.<\/p>\n<pre>void bad_boy_got_through()\r\n{\r\n  \/\/ Bad boy uses empty braces to sneak past the gate!\r\n  Package package(3, {});\r\n}\r\n<\/pre>\n<p>To prevent this, you need to give your private type an explicit constructor so it cannot be used implicitly.<\/p>\n<pre>class Package\r\n{\r\n  struct private_constructor\r\n    { <span style=\"color: blue;\">explicit private_constructor() = default;<\/span> };\r\n\r\npublic:\r\n  \/\/ Do not call constructor directly. Use CreatePackage instead.\r\n  Package(int id, private_constructor);\r\n\r\n  ...\r\n};\r\n<\/pre>\n<p>With this change, the bad boy has been foiled.<\/p>\n<pre>void bad_boy_foiled()\r\n{\r\n  \/\/ Can't sneak in with empty braces.\r\n  Package package(3, {});\r\n}\r\n<\/pre>\n<p>From Visual C++:<\/p>\n<pre style=\"whitespace: pre-wrap;\">error C2664: 'Package::Package(Package &amp;&amp;)': cannot convert argument 2 from 'initializer list' to 'Package::private_constructor'\r\n<\/pre>\n<p>From clang:<\/p>\n<pre>error: converting to 'Package::private_constructor' from initializer list would use explicit constructor 'constexpr Package::private_constructor::private_constructor()'\r\n<\/pre>\n<p>And the explicit constructor is inaccessible.<\/p>\n<pre>void bad_boy_foiled()\r\n{\r\n  \/\/ Can't use explicit constructor.\r\n  Package package(3, Package::private_constructor{});\r\n}\r\n\r\n\/\/ error: cannot access private struct\r\n<\/pre>\n<p>\u00b9 For example, <code>std::<\/code><code>make_<\/code><code>unique<\/code> requires that the object have a public constructor.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Braced initialization to the rescue, or denied.<\/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-105454","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Braced initialization to the rescue, or denied.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105454","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=105454"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105454\/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=105454"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105454"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105454"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}