On forcing all derived classes to implement a specific non-virtual method, part 2

The Old New Thing (Raymond Chen) News

Summary

The article explains how to use deleted functions in C++ to enforce derived class implementations of specific methods, improving compile-time error messages and developer guidance.

<p>Last time, we observed that <a title="On forcing all derived classes to implement a specific non-virtual method, part 1" href="https://devblogs.microsoft.com/oldnewthing/20260827-00/?p=112651"> one way to force all derived classes to implement a specific non-virtual method is simply not to implement it in the base class</a> and sit back and wait for the compile-time fireworks. I noted that we can do better than this, though.</p> <p>The problem is that the compiler tells you what is wrong, but the details are often buried in the &#8220;supplementary error information&#8221;, and it may not be obvious how to dig it out, or maybe you dig it out but you don&#8217;t understand how to fix it.</p> <p>You can steer people to the correct error by implementing the method as deleted.</p> <pre>// C++/WRL struct OneWayConverter { // Derived classes must implement Convert() HRESULT STDMETHODCALLTYPE Convert(IInspectable* value, ABI::Windows::UI::Xaml::Interop::TypeName targetType, IInspectable* parameter, HSTRING language, IInspectable** result) <span style="border: solid 1px currentcolor;">= delete</span>; // One-way converters cannot convert back HRESULT STDMETHODCALLTYPE ConvertBack(IInspectable* /*value*/, ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/, IInspectable* /*parameter*/, HSTRING /*language*/, IInspectable** result) { *result = nullptr; return E_NOTIMPL; } }; // C++/WinRT struct OneWayConverter { // Derived classes must implement Convert() winrt::Windows::Foundation::IInspectable Convert( winrt::Windows::Foundation::IInspectable const&amp; /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const&amp; /*targetType*/, winrt::Windows::Foundation::IInspectable const&amp; /*parameter*/, winrt::hstring const&amp; /*language*/) <span style="border: solid 1px currentcolor;">= delete</span>; // One-way converters cannot convert back winrt::Windows::Foundation::IInspectable ConvertBack( winrt::Windows::Foundation::IInspectable const&amp; /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const&amp; /*targetType*/, winrt::Windows::Foundation::IInspectable const&amp; /*parameter*/, winrt::hstring const&amp; /*language*/) { throw winrt::hresult_not_implemented(); } }; // Plain C++ analogous scenario struct OneWayConverter { // Derived classes must implement Convert() Color Convert(Widget const&amp;amp /*value*/) <span style="border: solid 1px currentcolor;">= delete</span>; // One-way converters cannot convert back Widget ConvertBack(Color const&amp; /*color*/) { throw std::exception("not implemented"); } }; </pre> <p>This has a few benefits.</p> <p>One is that the developer can see the exact function signature that they need to implement: It&#8217;s the one that got deleted in the base class.</p> <p>Another is that the error message takes them to the deleted function, and if they go to that line of code, they will see the comment that explains why it is deleted.</p> <pre style="white-space: pre-wrap;">winrt\windows.ui.xaml.data.h(1469,90): error C2280: 'winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable OneWayConverter::<wbr />Convert(<wbr />const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &amp;,<wbr />const winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName &amp;,<wbr />const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &amp;,<wbr />const winrt::<wbr />hstring &amp;)': attempting to reference a deleted function see declaration of 'OneWayConverter::Convert' test.cpp(60,9): 'winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable OneWayConverter::<wbr />Convert(<wbr />const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &amp;,const winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName &amp;,const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &amp;,<wbr />const winrt::<wbr />hstring &amp;)': <span style="border: solid 1px currentcolor;">function was explicitly deleted</span> ⟦ other error message spew the same as before ⟧ </pre> <p>Starting in C++26, you can do even better yet: You can put a custom message directly in the <code>delete</code>!</p> <pre>struct OneWayConverter { // Derived classes must implement Convert() winrt::Windows::Foundation::IInspectable Convert( winrt::Windows::Foundation::IInspectable const&amp; /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const&amp; /*targetType*/, winrt::Windows::Foundation::IInspectable const&amp; /*parameter*/, winrt::hstring const&amp; /*language*/) = delete<span style="border: solid 1px currentcolor;">("If you derive from OneWayConverter, you must implement Convert()")</span>; // One-way converters cannot convert back winrt::Windows::Foundation::IInspectable ConvertBack( winrt::Windows::Foundation::IInspectable const&amp; /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const&amp; /*targetType*/, winrt::Windows::Foundation::IInspectable const&amp; /*parameter*/, winrt::hstring const&amp; /*language*/) { throw winrt::hresult_not_implemented(); } }; </pre> <p>The Microsoft Visual C++ compiler doesn&#8217;t support this feature yet, but other compilers do, and they include the custom message in the primary error text.</p> <pre style="white-space: pre-wrap;">// clang error: attempt to use a deleted function: <span style="border: solid 1px currentcolor;">If you derive from OneWayConverter, you must implement Convert()</span> // gcc error: use of deleted function 'winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable OneWayConverter::<wbr />Convert(<wbr />winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable const&amp;,<wbr />winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName const&amp;,<wbr />winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable const&amp;,<wbr />winrt::<wbr />hstring const&amp;)': <span style="border: solid 1px currentcolor;">If you derive from OneWayConverter, you must implement Convert()</span> </pre> <p>While this works for C++/WinRT and plain C++, it doesn&#8217;t work for C++/WRL because WRL derives from the abstract base class, and you cannot delete a method implemented by a base class. (Presumably because the method is still callable by casting to the base class.)</p> <p>So the <code>delete</code> trick works only if your declaration is not an override of a base class declaration.</p> <p>Tweaking the implementation to provide better compiler error messages is another example of <a title="Compiler error message metaprogramming: Helping to find the conflicting macro definition" href="https://devblogs.microsoft.com/oldnewthing/20211206-00/?p=106002"> compiler error message metaprogramming</a>, which is one of the under-appreciated aspects of authoring a code library.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260828-00/?p=112654">On forcing all derived classes to implement a specific non-virtual method, part 2</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 08/29/26, 03:51 AM

# On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260828-00?p=112654](https://devblogs.microsoft.com/oldnewthing/20260828-00?p=112654) Last time, we observed that[one way to force all derived classes to implement a specific non\-virtual method is simply not to implement it in the base class](https://devblogs.microsoft.com/oldnewthing/20260827-00/?p=112651)and sit back and wait for the compile\-time fireworks\. I noted that we can do better than this, though\. The problem is that the compiler tells you what is wrong, but the details are often buried in the “supplementary error information”, and it may not be obvious how to dig it out, or maybe you dig it out but you don’t understand how to fix it\. You can steer people to the correct error by implementing the method as deleted\. ``` // C++/WRL struct OneWayConverter { // Derived classes must implement Convert() HRESULT STDMETHODCALLTYPE Convert(IInspectable* value, ABI::Windows::UI::Xaml::Interop::TypeName targetType, IInspectable* parameter, HSTRING language, IInspectable** result) = delete; // One-way converters cannot convert back HRESULT STDMETHODCALLTYPE ConvertBack(IInspectable* /*value*/, ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/, IInspectable* /*parameter*/, HSTRING /*language*/, IInspectable** result) { *result = nullptr; return E_NOTIMPL; } }; // C++/WinRT struct OneWayConverter { // Derived classes must implement Convert() winrt::Windows::Foundation::IInspectable Convert( winrt::Windows::Foundation::IInspectable const& /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, winrt::Windows::Foundation::IInspectable const& /*parameter*/, winrt::hstring const& /*language*/) = delete; // One-way converters cannot convert back winrt::Windows::Foundation::IInspectable ConvertBack( winrt::Windows::Foundation::IInspectable const& /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, winrt::Windows::Foundation::IInspectable const& /*parameter*/, winrt::hstring const& /*language*/) { throw winrt::hresult_not_implemented(); } }; // Plain C++ analogous scenario struct OneWayConverter { // Derived classes must implement Convert() Color Convert(Widget const&amp /*value*/) = delete; // One-way converters cannot convert back Widget ConvertBack(Color const& /*color*/) { throw std::exception("not implemented"); } }; ``` This has a few benefits\. One is that the developer can see the exact function signature that they need to implement: It’s the one that got deleted in the base class\. Another is that the error message takes them to the deleted function, and if they go to that line of code, they will see the comment that explains why it is deleted\. ``` winrt\windows.ui.xaml.data.h(1469,90): error C2280: 'winrt::Windows::Foundation::IInspectable OneWayConverter::Convert(const winrt::Windows::Foundation::IInspectable &,const winrt::Windows::UI::Xaml::Interop::TypeName &,const winrt::Windows::Foundation::IInspectable &,const winrt::hstring &)': attempting to reference a deleted function see declaration of 'OneWayConverter::Convert' test.cpp(60,9): 'winrt::Windows::Foundation::IInspectable OneWayConverter::Convert(const winrt::Windows::Foundation::IInspectable &,const winrt::Windows::UI::Xaml::Interop::TypeName &,const winrt::Windows::Foundation::IInspectable &,const winrt::hstring &)': function was explicitly deleted ⟦ other error message spew the same as before ⟧ ``` Starting in C\+\+26, you can do even better yet: You can put a custom message directly in the`delete`\! ``` struct OneWayConverter { // Derived classes must implement Convert() winrt::Windows::Foundation::IInspectable Convert( winrt::Windows::Foundation::IInspectable const& /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, winrt::Windows::Foundation::IInspectable const& /*parameter*/, winrt::hstring const& /*language*/) = delete("If you derive from OneWayConverter, you must implement Convert()"); // One-way converters cannot convert back winrt::Windows::Foundation::IInspectable ConvertBack( winrt::Windows::Foundation::IInspectable const& /*value*/, winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, winrt::Windows::Foundation::IInspectable const& /*parameter*/, winrt::hstring const& /*language*/) { throw winrt::hresult_not_implemented(); } }; ``` The Microsoft Visual C\+\+ compiler doesn’t support this feature yet, but other compilers do, and they include the custom message in the primary error text\. ``` // clang error: attempt to use a deleted function: If you derive from OneWayConverter, you must implement Convert() // gcc error: use of deleted function 'winrt::Windows::Foundation::IInspectable OneWayConverter::Convert(winrt::Windows::Foundation::IInspectable const&,winrt::Windows::UI::Xaml::Interop::TypeName const&,winrt::Windows::Foundation::IInspectable const&,winrt::hstring const&)': If you derive from OneWayConverter, you must implement Convert() ``` While this works for C\+\+/WinRT and plain C\+\+, it doesn’t work for C\+\+/WRL because WRL derives from the abstract base class, and you cannot delete a method implemented by a base class\. \(Presumably because the method is still callable by casting to the base class\.\) So the`delete`trick works only if your declaration is not an override of a base class declaration\. Tweaking the implementation to provide better compiler error messages is another example of[compiler error message metaprogramming](https://devblogs.microsoft.com/oldnewthing/20211206-00/?p=106002), which is one of the under\-appreciated aspects of authoring a code library\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.

Similar Articles

When can the C++ compiler devirtualize a call?

Hacker News Top

Explores when C++ compilers can devirtualize virtual function calls, covering cases like known dynamic types and final keyword, with comparisons across GCC, Clang, MSVC, and ICC.

C++26: Reducing undefined behaviour

Lobsters Hottest

C++26 introduces changes to reduce undefined behavior, notably making it ill-formed to delete a pointer to an incomplete type, improving program safety.