{"id":112651,"date":"2026-08-27T07:00:00","date_gmt":"2026-08-27T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112651"},"modified":"2026-08-28T00:07:56","modified_gmt":"2026-08-28T07:07:56","slug":"20260827-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260827-00\/?p=112651","title":{"rendered":"On forcing all derived classes to implement a specific non-virtual method, part 1"},"content":{"rendered":"<p>You may have a base class that implements only partial functionality and relies on the derived class to do the rest. How do you make sure that the derived class does the rest?<\/p>\n<p>For concreteness, let&#8217;s say that we are implementing <code>IValueConverter<\/code>, which has two methods:<\/p>\n<ul>\n<li><code>Convert()<\/code> to convert from the source to the destination.<\/li>\n<li><code>ConvertBack()<\/code> so that two-way conversions can convert from the destination to the source.<\/li>\n<\/ul>\n<p>Suppose you want to write a base class called <code>OneWayConverter<\/code>. Its implementation fails the <code>ConvertBack()<\/code> call, and you want to force the derived class to implement the forward conversion.<\/p>\n<pre>\/\/ C++\/WRL\r\n\r\nstruct WidgetColorConverter :\r\n    Microsoft::WRL::RuntimeClass&lt;\r\n        Microsoft::WRL::RuntimeClassFlags&lt;Microsoft::WRL::WinRt&gt;,\r\n        ABI::Windows::UI::Xaml::Data::IValueConverter&gt;,\r\n    OneWayConverter\r\n{\r\n    \/\/ Require the derived class to implement Convert somehow\r\n    HRESULT STDMETHODCALLTYPE Convert(IInspectable* value,\r\n        ABI::Windows::UI::Xaml::Interop::TypeName targetType,\r\n        IInspectable* parameter, HSTRING language,\r\n        IInspectable** result)\r\n    {\r\n        \u27e6 ... \u27e7\r\n    }\r\n};\r\n\r\n\/\/ C++\/WinRT\r\n\r\nstruct WidgetColorConverter :\r\n    winrt::implements&lt;WidgetColorConverter&gt;, OneWayConverter\r\n{\r\n    \/\/ Require the derived class to implement Convert somehow\r\n    winrt::Windows::Foundation::IInspectable\r\n        Convert(\r\n            winrt::Windows::Foundation::IInspectable const&amp; value,\r\n            winrt::Windows::UI::Xaml::Interop::TypeName const&amp; targetType,\r\n            winrt::Windows::Foundation::IInspectable const&amp; parameter,\r\n            winrt::hstring const&amp; language)\r\n    {\r\n        \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Plain C++ analogous scenario\r\nstruct WidgetColorConverter : OneWayConverter\r\n{\r\n    \/\/ Require the derived class to implement Convert somehow\r\n    Color Convert(Widget const&amp;amp value)\r\n    {\r\n        \u27e6 ... \u27e7\r\n    }\r\n};\r\n<\/pre>\n<p>During a code review, I saw that somebody tried to do this just by writing a comment.<\/p>\n<pre>\/\/ C++\/WRL\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must override this method.\r\n    HRESULT STDMETHODCALLTYPE Convert(IInspectable* \/*value*\/,\r\n        ABI::Windows::UI::Xaml::Interop::TypeName \/*targetType*\/,\r\n        IInspectable* \/*parameter*\/, HSTRING \/*language*\/,\r\n        IInspectable** result)\r\n    {\r\n        assert(false);\r\n        *result = nullptr;\r\n        return E_NOTIMPL;\r\n    }\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    HRESULT STDMETHODCALLTYPE ConvertBack(IInspectable* \/*value*\/,\r\n        ABI::Windows::UI::Xaml::Interop::TypeName \/*targetType*\/,\r\n        IInspectable* \/*parameter*\/, HSTRING \/*language*\/,\r\n        IInspectable** result)\r\n    {\r\n        *result = nullptr;\r\n        return E_NOTIMPL;\r\n    }\r\n};\r\n\r\n\/\/ C++\/WinRT\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must override this method.\r\n    winrt::Windows::Foundation::IInspectable\r\n        Convert(\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*value*\/,\r\n            winrt::Windows::UI::Xaml::Interop::TypeName const&amp; \/*targetType*\/,\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*parameter*\/,\r\n            winrt::hstring const&amp; \/*language*\/)\r\n    {\r\n        assert(false);\r\n        throw winrt::hresult_not_implemented();\r\n    }\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    winrt::Windows::Foundation::IInspectable\r\n        ConvertBack(\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*value*\/,\r\n            winrt::Windows::UI::Xaml::Interop::TypeName const&amp; \/*targetType*\/,\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*parameter*\/,\r\n            winrt::hstring const&amp; \/*language*\/)\r\n    {\r\n        throw winrt::hresult_not_implemented();\r\n    }\r\n};\r\n\r\n\/\/ Plain C++ analogous scenario\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must override this method.\r\n    Color Convert(Widget const&amp;amp \/*value*\/)\r\n    {\r\n        assert(false);\r\n        throw std::exception(\"not implemented\");\r\n    }\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    Widget ConvertBack(Color const&amp; \/*color*\/)\r\n    {\r\n        throw std::exception(\"not implemented\");\r\n    }\r\n};\r\n<\/pre>\n<p>I pointed out that they were doing too much work.<\/p>\n<p>The way to force somebody to implement a method in the derived class is simply not to implement the method in the base class in the first place.<\/p>\n<pre>\/\/ C++\/WRL\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must implement Convert()\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    HRESULT STDMETHODCALLTYPE ConvertBack(IInspectable* \/*value*\/,\r\n        ABI::Windows::UI::Xaml::Interop::TypeName \/*targetType*\/,\r\n        IInspectable* \/*parameter*\/, HSTRING \/*language*\/,\r\n        IInspectable** result)\r\n    {\r\n        *result = nullptr;\r\n        return E_NOTIMPL;\r\n    }\r\n};\r\n\r\n\/\/ C++\/WinRT\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must implement Convert()\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    winrt::Windows::Foundation::IInspectable\r\n        ConvertBack(\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*value*\/,\r\n            winrt::Windows::UI::Xaml::Interop::TypeName const&amp; \/*targetType*\/,\r\n            winrt::Windows::Foundation::IInspectable const&amp; \/*parameter*\/,\r\n            winrt::hstring const&amp; \/*language*\/)\r\n    {\r\n        throw winrt::hresult_not_implemented();\r\n    }\r\n};\r\n\r\n\/\/ Plain C++ analogous scenario\r\n\r\nstruct OneWayConverter\r\n{\r\n    \/\/ Derived classes must implement Convert()\r\n\r\n    \/\/ One-way converters cannot convert back\r\n    Widget ConvertBack(Color const&amp; \/*color*\/)\r\n    {\r\n        throw std::exception(\"not implemented\");\r\n    }\r\n};\r\n<\/pre>\n<p>The error message if they forget to implement it depends on the library.<\/p>\n<pre>\/\/ C++\/WRL\r\n\r\nstruct WidgetColorConverter :\r\n    Microsoft::WRL::RuntimeClass&lt;\r\n        Microsoft::WRL::RuntimeClassFlags&lt;Microsoft::WRL::WinRt&gt;,\r\n        OneWayConverter&gt;\r\n{\r\n    \/\/ Oops, forgot to implement Convert()\r\n};\r\n<\/pre>\n<p>The error occurs when you call <code>WRL::<wbr \/>Make<\/code> to try to create the defective <code>Widget\u00adColor\u00adConverter<\/code>.<\/p>\n<pre style=\"white-space: pre-wrap;\">wrl\\implements.h(2512,32): error C2259: 'WidgetColorConverter': cannot instantiate abstract class\r\n      see declaration of 'WidgetColorConverter'\r\n      due to following members:\r\n      wrl\\implements.h(2512,32):\r\n      'HRESULT ABI::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter::<wbr \/>Convert(IInspectable *,ABI::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Interop::<wbr \/>TypeName,<wbr \/>IInspectable *,<wbr \/>HSTRING,<wbr \/>IInspectable **)': is abstract\r\n      windows.ui.xaml.data.h(2778,59):\r\n      see declaration of 'ABI::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter::<wbr \/>Convert'\r\n      wrl\\implements.h(2512,32):\r\n      the template instantiation context (the oldest one first) is\r\n          test(41,30):\r\n          see reference to function template instantiation 'Microsoft::<wbr \/>WRL::<wbr \/>ComPtr&lt;<wbr \/>WidgetColorConverter&gt; Microsoft::<wbr \/>WRL::<wbr \/>Details::<wbr \/>Make&lt;WidgetColorConverter,&gt;(void)' being compiled\r\n<\/pre>\n<p>&#8220;Cannot instantiate abstract class due to the following members&#8221; is the standard error for failing to implement all the necessary pure virtual methods inherited from a base class, so one could expect that people who encounter this error will understand what it means.<\/p>\n<pre>\/\/ C++\/WinRT\r\n\r\nstruct WidgetColorConverter :\r\n    winrt::implements&lt;WidgetColorConverter, winrt::Windows::UI::Xaml::Data::IValueConverter&gt;,\r\n    OneWayConverter\r\n{\r\n    \/\/ Oops, forgot to implement Convert()\r\n};\r\n<\/pre>\n<p>The error occurs when you call <code>winrt::<wbr \/>make<\/code> to try to create the defective <code>Widget\u00adColor\u00adConverter<\/code>.<\/p>\n<pre style=\"white-space: pre-wrap;\">windows.ui.xaml.data.h(1469,90): error C2039: 'Convert': is not a member of 'WidgetColorConverter'\r\n      test.cpp(66,8):\r\n      see declaration of 'WidgetColorConverter'\r\n      windows.ui.xaml.data.h(1469,90):\r\n      the template instantiation context (the oldest one first) is\r\n          test.cpp(66,31):\r\n          see reference to class template instantiation 'winrt::<wbr \/>implements&lt;WidgetColorConverter,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter&gt;' being compiled\r\n          winrt\\base.h(8088,31):\r\n          see reference to class template instantiation 'winrt::<wbr \/>impl::<wbr \/>producers_base&lt;D,<wbr \/>std::tuple&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter&gt;&gt;' being compiled\r\n          with\r\n          [\r\n              D=WidgetColorConverter\r\n          ]\r\n          winrt\\base.h(6763,50):\r\n          see reference to class template instantiation 'winrt::<wbr \/>impl::<wbr \/>producer_convert&lt;D,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter,<wbr \/>void&gt;' being compiled\r\n          with\r\n          [\r\n              D=WidgetColorConverter\r\n          ]\r\n          winrt\\base.h(6734,31):\r\n          see reference to class template instantiation 'winrt::<wbr \/>impl::<wbr \/>producer&lt;D,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter,<wbr \/>void&gt;' being compiled\r\n          with\r\n          [\r\n              D=WidgetColorConverter\r\n          ]\r\n          winrt\\base.h(7137,23):\r\n          see reference to class template instantiation 'winrt::<wbr \/>impl::<wbr \/>produce&lt;D,I&gt;' being compiled\r\n          with\r\n          [\r\n              D=WidgetColorConverter,\r\n              I=winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter\r\n          ]\r\n          winrt\\windows.ui.xaml.data.h(1465,32):\r\n          while compiling class template member function 'int32_t winrt::<wbr \/>impl::<wbr \/>produce&lt;D,I&gt;::<wbr \/>Convert(<wbr \/>void *,<wbr \/>winrt::<wbr \/>impl::<wbr \/>struct_<wbr \/>Windows_<wbr \/>UI_<wbr \/>Xaml_<wbr \/>Interop_<wbr \/>TypeName,<wbr \/>void *,<wbr \/>void *,<wbr \/>void **) noexcept'\r\n          with\r\n          [\r\n              D=WidgetColorConverter,\r\n              I=winrt::<wbr \/>Windows::<wbr \/>UI::<wbr \/>Xaml::<wbr \/>Data::<wbr \/>IValueConverter\r\n          ]\r\n<\/pre>\n<p>&#8220;\u27e6Name\u27e7 is not a member of&#8221; is typical of a CRTP error, since the template is trying to call a method on the derived class, but it&#8217;s not there. Again, one could expect that people who encounter this error will understand what it means.<\/p>\n<p>For the plain C++ case, you might have this:<\/p>\n<pre>\/\/ Plain C++ analogous scenario\r\n\r\nstruct WidgetColorConverter :\r\n    OneWayConverter\r\n{\r\n    \/\/ Oops, forgot to implement Convert()\r\n};\r\n<\/pre>\n<p>And everything works great until somebody tries to call the <code>Convert<\/code> method on a <code>Widget\u00adColor\u00adConverter<\/code> and it&#8217;s not there.<\/p>\n<pre style=\"white-space: pre-wrap;\">test.cpp(79,20): error C2039: 'Convert': is not a member of 'WidgetColorConverter'\r\n<\/pre>\n<p>Again, this is a common error in C++ so you would hope that people understand what it means.<\/p>\n<p>Great, so we were able to convert all of these authoring errors into compile-time errors, thereby avoiding the danger that somebody will use the base class and fail to implement all of the expected methods.<\/p>\n<p>But wait, we can do better. We&#8217;ll look at this some more next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Don&#8217;t implement a stub. Just don&#8217;t implement it at all.<\/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-112651","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Don&#8217;t implement a stub. Just don&#8217;t implement it at all.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112651","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=112651"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112651\/revisions"}],"predecessor-version":[{"id":112652,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112651\/revisions\/112652"}],"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=112651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112651"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}