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 “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.</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& /*value*/,
winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/,
winrt::Windows::Foundation::IInspectable const& /*parameter*/,
winrt::hstring const& /*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& /*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*/) <span style="border: solid 1px currentcolor;">= delete</span>;
// One-way converters cannot convert back
Widget ConvertBack(Color const& /*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’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 &,<wbr />const winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName &,<wbr />const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &,<wbr />const winrt::<wbr />hstring &)': 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 &,const winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName &,const winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable &,<wbr />const winrt::<wbr />hstring &)': <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& /*value*/,
winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/,
winrt::Windows::Foundation::IInspectable const& /*parameter*/,
winrt::hstring const& /*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& /*value*/,
winrt::Windows::UI::Xaml::Interop::TypeName const& /*targetType*/,
winrt::Windows::Foundation::IInspectable const& /*parameter*/,
winrt::hstring const& /*language*/)
{
throw winrt::hresult_not_implemented();
}
};
</pre>
<p>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.</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&,<wbr />winrt::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Interop::<wbr />TypeName const&,<wbr />winrt::<wbr />Windows::<wbr />Foundation::<wbr />IInspectable const&,<wbr />winrt::<wbr />hstring const&)': <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’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>
# 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& /*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 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\.
The article discusses techniques in C++ to enforce derived classes to implement a specific non-virtual method, using examples from C++/WRL and C++/WinRT, and critiques a code review scenario where comments were used instead of proper enforcement.
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 introduces changes to reduce undefined behavior, notably making it ill-formed to delete a pointer to an incomplete type, improving program safety.
The article explains how to reduce C++ template bloat by factoring out type-dependent portions of functions, using a practical example with Windows DispatcherQueueHandler to de-templatize code.
Raymond Chen continues his series on creating a fake agile wrapper in COM, explaining how to move non-marshalable objects by forwarding references into the force_marshal wrapper and discussing C++/WinRT deduction guides.