{"id":106828,"date":"2022-07-04T07:00:00","date_gmt":"2022-07-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106828"},"modified":"2024-12-20T07:14:50","modified_gmt":"2024-12-20T15:14:50","slug":"20220704-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220704-00\/?p=106828","title":{"rendered":"The case of the constructor that was being ignored"},"content":{"rendered":"<p>When I pointed out <a title=\"How can I build a URL query string in the Windows Runtime?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220630-00\/?p=106805\"> a way to build URL query strings in the Windows Runtime<\/a>, the customer reported that it didn&#8217;t work.<\/p>\n<pre>#include &lt;winrt\/Windows.Web.Http.h&gt;\r\n\r\nvoid test()\r\n{\r\n    auto encoder = HttpFormUrlEncodedContent({\r\n        { L\"v\", L\"dQw4w9WgXcQ\" },\r\n        { L\"t\", L\"43s\" },\r\n    });\r\n}\r\n<\/pre>\n<p>This failed with the error<\/p>\n<pre style=\"white-space: pre-wrap;\">error C2440: '&lt;function-style-cast&gt;': cannot convert from 'initializer list' to 'winrt::Windows::Web::Http::HttpFormUrlEncodedContent'\r\nmessage : No constructor could take the source type, or constructor overload resolution was ambiguous\r\n<\/pre>\n<p>Let&#8217;s start debugging.<\/p>\n<p>If you do a &#8220;Go to definition&#8221; on the <code>Http\u00adForm\u00adUrl\u00adEncoded\u00adContent<\/code> in Visual Studio, you are taken to the class definition, which happens to be in the header file <code>impl\/<wbr \/>Windows.<wbr \/>Web.<wbr \/>Http.<wbr \/>2.h<\/code>.<\/p>\n<pre>    struct __declspec(empty_bases) HttpFormUrlEncodedContent :\r\n        Windows::Web::Http::IHttpContent,\r\n        impl::require&lt;HttpFormUrlEncodedContent, Windows::Foundation::IStringable&gt;\r\n    {\r\n        HttpFormUrlEncodedContent(std::nullptr_t) noexcept {}\r\n        HttpFormUrlEncodedContent(void* ptr, take_ownership_from_abi_t) noexcept :\r\n            Windows::Web::Http::IHttpContent(ptr, take_ownership_from_abi) {}\r\n        explicit HttpFormUrlEncodedContent(\r\n            param::iterable&lt;Windows::Foundation::Collections::\r\n                IKeyValuePair&lt;hstring, hstring&gt;&gt; const&amp; content);\r\n    };\r\n<\/pre>\n<p>Let&#8217;s look at these constructors one at a time.<\/p>\n<p>First up is the <code>nullptr<\/code> constructor for creating an empty <code>Http\u00adForm\u00adUrl\u00adEncoded\u00adContent<\/code> smart pointer.<\/p>\n<p>Next is the <code>take_<wbr \/>ownership_<wbr \/>from_<wbr \/>abi<\/code> constructor for creating a <code>Http\u00adForm\u00adUrl\u00adEncoded\u00adContent<\/code> that takes over ownership of the object from a pointer obtained at the ABI layer. It is a two-parameter constructor and therefore would never be considered since we are calling the constructor with one parameter.<\/p>\n<p>Last is the one we are trying to call: It takes a <code>param::<wbr \/>iterable<\/code> of <code>IKeyValuePair&lt;hstring, hstring&gt;<\/code>.<\/p>\n<p>There are also two implicitly defined constructors: The copy and move constructor. Those candidates look like this:<\/p>\n<pre>        HttpFormUrlEncodedContent(HttpFormUrlEncodedContent const&amp;) = default;\r\n        HttpFormUrlEncodedContent(HttpFormUrlEncodedContent &amp;&amp;) = default;\r\n<\/pre>\n<p>Okay, so we have four candidates that survived the arity check.<\/p>\n<ul>\n<li><code>nullptr<\/code> constructor.<\/li>\n<li><code>param::iterable<\/code> constructor.<\/li>\n<li>copy constructor.<\/li>\n<li>move constructor.<\/li>\n<\/ul>\n<p>The error messages says that the compiler could not find a suitable constructor, so we have to think about why the <code>param::iterable<\/code> constructor wasn&#8217;t chosen. We expect it to be chosen because <code>param::iterable<\/code> has a conversion constructor that takes an <code>initializer_<wbr \/>list<\/code>. Why isn&#8217;t that conversion being used?<\/p>\n<p>I could not reproduce the error in my test project, so I asked the customer to send me theirs. I ran the file through the preprocessor so I could see exactly what the compiler saw, thinking that maybe the customer had some <code>#ifdef<\/code> or other weird configuration.<\/p>\n<p>I searched the preprocessed file for the <code>param::iterable<\/code> constructor.<\/p>\n<p>And it wasn&#8217;t there!<\/p>\n<p>The preprocessed file had a forward declaration for <code>param::iterable<\/code>, but no definition. That explains why the compiler couldn&#8217;t convert the <code>initializer_<wbr \/>list<\/code> to a <code>param::iterable<\/code>: Because the conversion constructor hadn&#8217;t yet been declared!<\/p>\n<p>The <code>param::iterable<\/code> template class is defined in the header file <code>winrt\/Windows.Foundation.Collections.h<\/code>, following the C++\/WinRT rule that you must explicitly include the header files for any namespaces you use. We are using the <code>Windows::<wbr \/>Foundation::<wbr \/>Collections<\/code> namespace because that&#8217;s where the <code>IIterable<\/code> class resides, and that is the projected type of the parameter that the <code>Http\u00adForm\u00adUrl\u00adEncoded\u00adContent<\/code> constructor accepts.<\/p>\n<p>This question started out as a &#8220;C++\/WinRT problem&#8221; (which is how I got roped into it), but all of the debugging just treated it as a &#8220;C++ problem&#8221;: It turns out that if there&#8217;s a particular constructor you want to use, you should make sure the parameter types are defined.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Retracing the compiler&#8217;s steps.<\/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-106828","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Retracing the compiler&#8217;s steps.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106828","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=106828"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106828\/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=106828"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106828"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106828"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}