{"id":108839,"date":"2023-09-29T07:00:00","date_gmt":"2023-09-29T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108839"},"modified":"2023-09-29T10:31:48","modified_gmt":"2023-09-29T17:31:48","slug":"20230929-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230929-00\/?p=108839","title":{"rendered":"Template meta-programming: Avoiding saying a type before it is complete"},"content":{"rendered":"<p>As we noted last time, <a title=\"C++\/WinRT gotcha: get_strong() will produce a broken strong reference if destruction has already begun\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230928-53\/?p=108833\"> C++\/WinRT&#8217;s <code>get_strong()<\/code> will produce a broken strong reference if destruction has already begun<\/a>. The C++ standard library&#8217;s <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> solves this problem by saving a weak reference in the object itself. But if you tried the analogous trick in versions of C++\/WinRT prior to <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/releases\/tag\/2.0.211028.7\"> 2.0.211028.7<\/a>, you got a weird compiler error.<\/p>\n<pre style=\"white-space: pre-wrap;\">struct Me : winrt::implements&lt;Me, winrt::IInspectable&gt;\r\n{\r\n    winrt::weak_ref&lt;Me&gt; m_weakSelf;\r\n};\r\n\r\n\/\/ msvc\r\ntype_traits(1173,28): error C2139: 'Me': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_base_of'\r\ntest.h(16,8): message : see declaration of 'Me'\r\nbase.h(1963,45): message : see reference to variable template 'const bool is_base_of_v&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;' being compiled\r\nbase.h(4119,24): message : see reference to alias template instantiation 'winrt::<wbr \/>impl::<wbr \/>com_ref&lt;T&gt;' being compiled\r\nwith\r\n[\r\n    T=Me\r\n]\r\nTest.cpp(19,25): message : see reference to class template instantiation 'winrt::<wbr \/>weak_ref&lt;D&gt;' being compiled\r\nwith\r\n[\r\n    D=Me\r\n]\r\n\r\n\/\/ gcc\r\ntype_traits: In instantiation of 'struct std::is_base_of&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;':\r\ntype_traits:3282:69:   required from 'constexpr const bool std::is_base_of_v&lt;winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;'\r\nrequired from 'struct winrt::weak_ref&lt;Me&gt;'\r\nrequired from here\r\ntype_traits:1447:38: error: invalid use of incomplete type 'struct Me'\r\n|     : public integral_constant&lt;bool, __is_base_of(<wbr \/>_Base, _Derived)&gt;\r\n|                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nnote: forward declaration of 'struct Me'\r\n| struct Me : winrt::implements&lt;Me, winrt::IInspectable&gt;\r\n|        ^^\r\ntype_traits: In instantiation of 'constexpr const bool std::is_base_of_v&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;':\r\nrequired from 'struct winrt::weak_ref&lt;Me&gt;'\r\nrequired from here\r\ntype_traits:3282:69: error: 'value' is not a member of 'std::is_base_of&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;'\r\n|   inline constexpr bool is_base_of_v = is_base_of&lt;<wbr \/>_Base, _Derived&gt;::value;\r\n|                                                                     ^~~~~\r\n\r\n\/\/ clang\r\ntype_traits:3345:68: error: incomplete type 'Me' used in type trait expression\r\n  inline constexpr bool is_base_of_v = __is_base_of(_Base, _Derived);\r\n                                                                   ^\r\nnote: in instantiation of variable template specialization 'std::is_base_of_v&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown, Me&gt;' requested here\r\n        std::is_base_of_v&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown,T&gt;, int, int&gt; v);\r\n             ^\r\nnote: in instantiation of template class 'winrt::<wbr \/>weak_ref&lt;Me&gt;' requested here\r\n    winrt::weak_ref&lt;Me&gt; m_weakSelf;\r\n                        ^\r\nnote: definition of 'Me' is not complete until the closing '}'\r\nstruct Me : winrt::implements&lt;Me, winrt::IInspectable&gt;\r\n       ^\r\n<\/pre>\n<p>In this case, clang is the one that pinpoints the problem: &#8220;Definition of <code>Me<\/code> is not complete until the closing brace.&#8221;<\/p>\n<p>The problem is that <code>weak_ref&lt;T&gt;<\/code> requires that <code>T<\/code> be a complete type because it has a constructor that relies on the completeness of <code>T<\/code>:<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    weak_ref(<span style=\"border: dashed 1px currentcolor;\">impl::com_ref&lt;T&gt;<\/span> const&amp; object)\r\n    {\r\n        \u27e6 implementation elided \u27e7\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n};\r\n<\/pre>\n<p>The <code>impl::<wbr \/>com_ref<\/code> template type is an internal C++\/WinRT helper. We can peek at its definition:<\/p>\n<pre>template &lt;typename T&gt;\r\nusing com_ref = std::conditional_t&lt;\r\n    std::is_base_of_v&lt;Windows::Foundation::IUnknown, T&gt;,\r\n    T,\r\n    com_ptr&lt;T&gt;&gt;;\r\n<\/pre>\n<p>In words, a <code>com_ref&lt;T&gt;<\/code> is just a <code>T<\/code> if <code>T<\/code> is itself a projected type. Otherwise, it&#8217;s a <code>com_ptr&lt;T&gt;<\/code>.<\/p>\n<p>Now, when you write <code>weak_ref&lt;T&gt;<\/code>, this instantiates the template, and the compiler generates declarations for all of the members. One of those members is the second constructor that takes a <code>impl::<wbr \/>com_ref&lt;T&gt;<\/code> as a parameter. And that&#8217;s where the error occurs, because in order to know what <code>impl::<wbr \/>com_ref&lt;T&gt;<\/code> means, the compiler has to see whether <code>T<\/code> has <code>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown<\/code> as a base class, and that requires that <code>T<\/code> be a complete type.<\/p>\n<p>We can solve this problem by templating the second constructor.<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">template&lt;typename U&gt;<\/span>\r\n    weak_ref(<span style=\"border: solid 1px currentcolor;\">U&amp;&amp;<\/span> object)\r\n    {\r\n        \u27e6 implementation elided \u27e7\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n};\r\n<\/pre>\n<p>This defers the instantiation of the second constructor until somebody tries to call it, which will (we hope) happen after <code>T<\/code> is a complete type.<\/p>\n<p>But wait, we&#8217;re not done yet.<\/p>\n<p>For one thing, the original constructor specified its parameter as <code>impl::<wbr \/>com_ref&lt;T&gt;<\/code>, which means that if the caller passed a braced list, that braced list is used to construct a <code>impl::<wbr \/>com_ref&lt;T&gt;<\/code>. But we don&#8217;t get that effect with the forwarding reference. Forwarding references received braced lists as a <code>initializer_<wbr \/>list&lt;X&gt;<\/code> for some type <code>X<\/code>. In order to tell the compiler, &#8220;If you see a braced list, please treat it as the constructor parameters to a <code>impl::<wbr \/>com_ref&lt;T&gt;<\/code>,&#8221; we specify a default type:<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    template&lt;typename U <span style=\"border: solid 1px currentcolor;\">= impl::com_ref&lt;T&gt;<\/span>&gt;\r\n    weak_ref(U&amp;&amp; object)\r\n    {\r\n        \u27e6 implementation elided \u27e7\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n};\r\n<\/pre>\n<p>Now, the original implementation assumed that <code>object<\/code> was a <code>com_ref&lt;T&gt;<\/code>, so if the inbound parameter isn&#8217;t one, we need to convert it.<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    template&lt;typename U = impl::com_ref&lt;T&gt;&gt;\r\n    weak_ref(U&amp;&amp; <span style=\"border: solid 1px currentcolor;\">objectArg<\/span>)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">impl::com_ref&lt;T&gt; const&amp; object = objectArg;<\/span>\r\n\r\n        \u27e6 implementation elided \u27e7\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n};\r\n<\/pre>\n<p>There&#8217;s a subtlety here, though. The conversion from <code>objectArg<\/code> to <code>object<\/code> is an explicit conversion, but parameter conversions use only implicit conversions. We can use SFINAE to limit ourselves to types that support implicit conversion to <code>com_ref&lt;T&gt;<\/code>.<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    template&lt;typename U = impl::com_ref&lt;T&gt;,\r\n             <span style=\"border: solid 1px currentcolor; border-bottom: none;\">typename = std::enable_if_t&lt;    <\/span>\r\n             <span style=\"border: 1px currentcolor; border-style: none solid;\">   std::is_convertible_v&lt;       <\/span>\r\n             <span style=\"border: 1px currentcolor; border-style: none solid;\">       U&amp;&amp;,                     <\/span>\r\n             <span style=\"border: solid 1px currentcolor; border-top: none;\">       impl::com_ref&lt;T&gt; const&amp;&gt;&gt;<\/span>&gt;\r\n    weak_ref(U&amp;&amp; objectArg)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">impl::com_ref&lt;T&gt; const&amp; object = objectArg;<\/span>\r\n\r\n        \u27e6 implementation elided \u27e7\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n};\r\n<\/pre>\n<p>This revision also solves another problem: Without SFINAE, our templated <code>weak_ref<\/code> constructor would also be used as the copy and move constructor, rather than using the compiler-generated default versions. Fortunately, the SFINAE rejects <code>weak_ref const&amp;<\/code> and <code>weak_ref&amp;&amp;<\/code> (since neither is convertible to <code>com_ref&lt;T&gt;<\/code>), so adding the SFINAE also magically restores the copy and move constructors.<\/p>\n<p>Finally, to avoid code size explosion due to each specialization producing a different conversion, we factor the original constructor into a helper that takes only a <code>com_ref&lt;&gt;<\/code>. That way, the bulk of the code is shared among all the constructors.<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct weak_ref\r\n{\r\n    weak_ref(std::nullptr_t = nullptr) noexcept {}\r\n\r\n    template&lt;typename U = impl::com_ref&lt;T&gt;,\r\n             typename = std::enable_if_t&lt;\r\n                std::is_convertible_v&lt;\r\n                    U&amp;&amp;,\r\n                    impl::com_ref&lt;T&gt; const&amp;&gt;&gt;&gt;\r\n    weak_ref(U&amp;&amp; object)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">from_com_ref(static_cast&lt;impl::com_ref&lt;T&gt; const&amp;&gt;(object));<\/span>\r\n    }\r\n\r\n    \u27e6 other members \u27e7\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">template&lt;typename U&gt;         <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">void from_com_ref(U&amp;&amp; object)<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">{                            <\/span>\r\n        \u27e6 implementation elided \u27e7\r\n    <span style=\"border: solid 1px currentcolor;\">}                            <\/span>\r\n};\r\n<\/pre>\n<p>Again, the <code>from_com_ref<\/code> method is templated so that it is not instantiated immediately, since it is not allowed to say that its parameter is a <code>com_ref&lt;T&gt;<\/code>. Instead, we secretly accept a <code>com_ref&lt;T&gt;<\/code> by using a parlor trick from some time ago: <a title=\"C++ template parlor tricks: Using a type before it is defined\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221202-00\/?p=107532\"> We accept a templated parameter even though in practice, only one type ever gets passed<\/a>.<\/p>\n<p>What this all means is that starting in C++\/WinRT version <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/releases\/tag\/2.0.211028.7\"> 2.0.211028.7<\/a>, you can have a class hold a weak reference to itself.<\/p>\n<pre style=\"white-space: pre-wrap;\">struct Me : winrt::implements&lt;Me, winrt::IInspectable&gt;\r\n{\r\n    winrt::weak_ref&lt;Me&gt; m_weakSelf = get_weak();\r\n};\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Deferring use until after the class is defined.<\/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-108839","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Deferring use until after the class is defined.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108839","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=108839"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108839\/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=108839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}