{"id":109552,"date":"2024-03-19T07:00:00","date_gmt":"2024-03-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109552"},"modified":"2024-03-19T11:32:42","modified_gmt":"2024-03-19T18:32:42","slug":"20240319-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240319-00\/?p=109552","title":{"rendered":"How well does C++\/WinRT <CODE>com_ptr<\/CODE> support class template argument deduction (CTAD)?"},"content":{"rendered":"<p>Continuing our investigation of <a title=\"Class template argument deduction (CTAD) and C++ COM wrappers, part 1: Initial explorations\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240311-00\/?p=109521\"> which C++ COM wrappers support class template argument deduction<\/a> (CTAD), next up is the C++\/WinRT <code>winrt::<wbr \/>com_ptr<\/code>.<\/p>\n<p>This one is easy: C++\/WinRT&#8217;s <code>com_ptr<\/code> doesn&#8217;t support CTAD, and it doesn&#8217;t even try. There is no <code>com_ptr&lt;T&gt;<\/code> constructor that takes a <code>T*<\/code>. If you want a <code>com_ptr<\/code> to copy an existing pointer, you call <code>com_ptr::<wbr \/>copy_from<\/code>:<\/p>\n<pre>IWidget* p;\r\n\r\nwinrt::com_ptr&lt;IWidget&gt; smart;\r\nsmart.copy_from(p);\r\n<\/pre>\n<p>C++\/WinRT eschews the constructor that makes a copy of an inbound pointer on the theory that it&#8217;s unclear whether it is taking ownership of the pointer or merely sharing it.<\/p>\n<p>The library author could have provided a deduction guide for the &#8220;take ownership&#8221; constructor:<\/p>\n<pre>template&lt;typename T&gt;\r\ncom_ptr(T*, take_ownership_from_abi_t) -&gt; com_ptr&lt;T&gt;;\r\n<\/pre>\n<p>As a consumer, though, you shouldn&#8217;t be creating deduction guides for somebody else&#8217;s classes. Instead, you can use a maker function.<\/p>\n<pre>template&lt;typename T&gt;\r\nwinrt::com_ptr&lt;\r\n    std::enable_if_t&lt;std::&lt;is_base_of_v&lt;::IUnknown, T&gt;, T&gt;&gt;\r\n    make_com_ptr(T* p, winrt::take_ownership_from_abi_t)\r\n{\r\n    return { p, winrt::take_ownership_from_abi };\r\n}\r\n<\/pre>\n<p>There is some extra magic in the return value to activate the function only if <code>T<\/code> derives from <code>::IUnknown<\/code>. This avoids accidentally creating a <code>com_ptr<\/code> from a C++\/WinRT ABI pointer (which are in the <code>impl<\/code> namespace and are therefore off-limits).<\/p>\n<p>If you feel so brave, you can also create a maker function that copies the ABI pointer.<\/p>\n<pre>template&lt;typename T&gt;\r\nwinrt::com_ptr&lt;T&gt;\r\n    std::enable_if_t&lt;std::&lt;is_base_of_v&lt;::IUnknown, T&gt;, T&gt;&gt;\r\n    make_com_ptr_from_copy(T* p)\r\n{\r\n    winrt::com_ptr&lt;T&gt; result;\r\n    result.copy_from(p);\r\n    return result;\r\n}\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: The last time I made a survey of C++ COM wrappers, someone asked me to include C++\/CX in the list. But C++\/CX doesn&#8217;t support wrappers around classic COM interfaces; it supports only Windows Runtime interfaces. Therefore, the answer for C++\/CX is always &#8220;Not supported. Nothing to see here.&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It doesn&#8217;t even try, and probably doesn&#8217;t want you to do 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-109552","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It doesn&#8217;t even try, and probably doesn&#8217;t want you to do it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109552","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=109552"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109552\/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=109552"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109552"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109552"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}