{"id":109526,"date":"2024-03-12T07:00:00","date_gmt":"2024-03-12T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109526"},"modified":"2024-03-12T07:40:43","modified_gmt":"2024-03-12T14:40:43","slug":"20240312-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240312-00\/?p=109526","title":{"rendered":"How well does <CODE>_com_ptr_t<\/CODE> support class template argument deduction (CTAD)?"},"content":{"rendered":"<p>We saw earlier that <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\"> the <code>_com_ptr_t<\/code> class template doesn&#8217;t work well with class template argument deduction<\/a> (CTAD).<\/p>\n<p>Template argument deduction fails on this constructor:<\/p>\n<pre>template&lt;typename _IIID&gt; class _com_ptr_t {\r\npublic:\r\n    using ThisIIID = _IIID;\r\n    using Interface = typename _IIID::Interface;\r\n\r\n    _com_ptr_t(Interface* pInterface); \/\/ this one\r\n\r\n    \u27e6 other stuff \u27e7\r\n};\r\n<\/pre>\n<p>The problem is that the compiler can&#8217;t figure out what <code>_IIID<\/code> to use that will produce the desired <code>Interface<\/code>. After all, there are an infinite number of such classes.<\/p>\n<pre>struct SomeRandomClass\r\n{\r\n    using Interface = ::IWidget;\r\n};\r\n\r\nstruct AnotherRandomClass\r\n{\r\n    using Interface = ::IWidget;\r\n};\r\n<\/pre>\n<p>Both of these random classes are viable candidates for the template type argument <code>_IIID<\/code>. Clearly there is no reason for the compiler to choose one over the other. The general rule is that the compiler will try to deduce the template arguments from the parameter list, but this requires that the parameter list involves the template arguments at all!<\/p>\n<p>The library intends the <code>_IIID<\/code> to be a specialization of a helper type named <code>_com_IIID<\/code>, where <code>IIID<\/code> presumably stands for <u>I<\/u>nterface and <u>I<\/u>nterface <u>id<\/u>entifier.<\/p>\n<pre>template&lt;typename _Interface, const IID* _IID \/*= &amp;__uuidof(_Interface)*\/&gt;\r\nclass _com_IIID {\r\npublic:\r\n    typedef _Interface Interface;\r\n\r\n    static const IID&amp; GetIID() noexcept\r\n    {\r\n        return *_IID;\r\n    }\r\n\r\n    \u27e6 other stuff \u27e7\r\n};\r\n<\/pre>\n<p>If the library had the ability to see into the C++17 future, it could have provided a deduction guide:<\/p>\n<pre>template&lt;typename T&gt; _com_ptr_t(T*)\r\n    -&gt; _com_ptr_t&lt;_com_IIID&lt;T, &amp;__uuidof(T)&gt;&gt;;\r\n<\/pre>\n<p>This tells the compiler that if it sees a <code>_com_ptr_t(T*)<\/code>, it should construct a <code>_com_ptr_t&lt;_com_IIID&lt;T, &amp;__uuidof(T)&gt;&gt;<\/code>.<\/p>\n<p>As a rule of thumb, you shouldn&#8217;t create deduction guides for types defined in someone else&#8217;s library, because a future version of the library might add a deduction guide, and now you have a conflict between your deduction guide and the library&#8217;s. Deduction guides can be thought of as constructor metadata, and the class controls its own constructors.<\/p>\n<p>In practice, this lack of CTAD support is not an issue because you don&#8217;t use <code>_com_ptr_t<\/code> directly; instead, you use the <code>_COM_<wbr \/>SMARTPTR_<wbr \/>TYPEDEF<\/code> macro to define a custom type name for your specialized smart pointer, and then you use that custom type name from then on.<\/p>\n<pre>_COM_SMARTPTR_TYPEDEF(IWidget, __uuidof(IWidget));\r\n\r\nIWidget* p;\r\nauto smart = IWidgetPtr(p);\r\n<\/pre>\n<p>So really, this was much ado about nothing.<\/p>\n<p>But what if you really want to avoid typing out the interface name when using smart pointers based on <code>_com_ptr_t<\/code>? We&#8217;ll find the answer when we look at MFC <code>IPTR<\/code>\/<code>CIP<\/code> next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Not very well, thanks to storing the pointer and IID pair in a helper type.<\/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-109526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Not very well, thanks to storing the pointer and IID pair in a helper type.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109526","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=109526"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109526\/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=109526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}