惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
D
Docker
J
Java Code Geeks
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
The GitHub Blog
The GitHub Blog
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
T
Tailwind CSS Blog
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
Google DeepMind News
Google DeepMind News
腾讯CDC
罗磊的独立博客
U
Unit 42
爱范儿
爱范儿
Vercel News
Vercel News
MyScale Blog
MyScale Blog

The Old New Thing

Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing Forcing an ARM64X executable to run as a specific architecture - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4 - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3 - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 2 - The Old New Thing
On forcing all derived classes to implement a specific no...
Raymond Chen · 2026-08-27 · via The Old New Thing

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?

For concreteness, let’s say that we are implementing IValueConverter, which has two methods:

  • Convert() to convert from the source to the destination.
  • ConvertBack() so that two-way conversions can convert from the destination to the source.

Suppose you want to write a base class called OneWayConverter. Its implementation fails the ConvertBack() call, and you want to force the derived class to implement the forward conversion.

// C++/WRL

struct WidgetColorConverter :
    Microsoft::WRL::RuntimeClass<
        Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRt>,
        ABI::Windows::UI::Xaml::Data::IValueConverter>,
    OneWayConverter
{
    // Require the derived class to implement Convert somehow
    HRESULT STDMETHODCALLTYPE Convert(IInspectable* value,
        ABI::Windows::UI::Xaml::Interop::TypeName targetType,
        IInspectable* parameter, HSTRING language,
        IInspectable** result)
    {
        ⟦ ... ⟧
    }
};

// C++/WinRT

struct WidgetColorConverter :
    winrt::implements<WidgetColorConverter>, OneWayConverter
{
    // Require the derived class to implement Convert somehow
    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)
    {
        ⟦ ... ⟧
    }
}

// Plain C++ analogous scenario
struct WidgetColorConverter : OneWayConverter
{
    // Require the derived class to implement Convert somehow
    Color Convert(Widget const&amp value)
    {
        ⟦ ... ⟧
    }
};

During a code review, I saw that somebody tried to do this just by writing a comment.

// C++/WRL

struct OneWayConverter
{
    // Derived classes must override this method.
    HRESULT STDMETHODCALLTYPE Convert(IInspectable* /*value*/,
        ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/,
        IInspectable* /*parameter*/, HSTRING /*language*/,
        IInspectable** result)
    {
        assert(false);
        *result = nullptr;
        return E_NOTIMPL;
    }

    // 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 override this method.
    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*/)
    {
        assert(false);
        throw winrt::hresult_not_implemented();
    }

    // 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 override this method.
    Color Convert(Widget const&amp /*value*/)
    {
        assert(false);
        throw std::exception("not implemented");
    }

    // One-way converters cannot convert back
    Widget ConvertBack(Color const& /*color*/)
    {
        throw std::exception("not implemented");
    }
};

I pointed out that they were doing too much work.

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.

// C++/WRL

struct OneWayConverter
{
    // Derived classes must implement Convert()

    // 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()

    // 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()

    // One-way converters cannot convert back
    Widget ConvertBack(Color const& /*color*/)
    {
        throw std::exception("not implemented");
    }
};

The error message if they forget to implement it depends on the library.

// C++/WRL

struct WidgetColorConverter :
    Microsoft::WRL::RuntimeClass<
        Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRt>,
        OneWayConverter>
{
    // Oops, forgot to implement Convert()
};

The error occurs when you call WRL::Make to try to create the defective WidgetColorConverter.

wrl\implements.h(2512,32): error C2259: 'WidgetColorConverter': cannot instantiate abstract class
      see declaration of 'WidgetColorConverter'
      due to following members:
      wrl\implements.h(2512,32):
      'HRESULT ABI::Windows::UI::Xaml::Data::IValueConverter::Convert(IInspectable *,ABI::Windows::UI::Xaml::Interop::TypeName,IInspectable *,HSTRING,IInspectable **)': is abstract
      windows.ui.xaml.data.h(2778,59):
      see declaration of 'ABI::Windows::UI::Xaml::Data::IValueConverter::Convert'
      wrl\implements.h(2512,32):
      the template instantiation context (the oldest one first) is
          test(41,30):
          see reference to function template instantiation 'Microsoft::WRL::ComPtr<WidgetColorConverter> Microsoft::WRL::Details::Make<WidgetColorConverter,>(void)' being compiled

“Cannot instantiate abstract class due to the following members” 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.

// C++/WinRT

struct WidgetColorConverter :
    winrt::implements<WidgetColorConverter, winrt::Windows::UI::Xaml::Data::IValueConverter>,
    OneWayConverter
{
    // Oops, forgot to implement Convert()
};

The error occurs when you call winrt::make to try to create the defective WidgetColorConverter.

windows.ui.xaml.data.h(1469,90): error C2039: 'Convert': is not a member of 'WidgetColorConverter'
      test.cpp(66,8):
      see declaration of 'WidgetColorConverter'
      windows.ui.xaml.data.h(1469,90):
      the template instantiation context (the oldest one first) is
          test.cpp(66,31):
          see reference to class template instantiation 'winrt::implements<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter>' being compiled
          winrt\base.h(8088,31):
          see reference to class template instantiation 'winrt::impl::producers_base<D,std::tuple<winrt::Windows::UI::Xaml::Data::IValueConverter>>' being compiled
          with
          [
              D=WidgetColorConverter
          ]
          winrt\base.h(6763,50):
          see reference to class template instantiation 'winrt::impl::producer_convert<D,winrt::Windows::UI::Xaml::Data::IValueConverter,void>' being compiled
          with
          [
              D=WidgetColorConverter
          ]
          winrt\base.h(6734,31):
          see reference to class template instantiation 'winrt::impl::producer<D,winrt::Windows::UI::Xaml::Data::IValueConverter,void>' being compiled
          with
          [
              D=WidgetColorConverter
          ]
          winrt\base.h(7137,23):
          see reference to class template instantiation 'winrt::impl::produce<D,I>' being compiled
          with
          [
              D=WidgetColorConverter,
              I=winrt::Windows::UI::Xaml::Data::IValueConverter
          ]
          winrt\windows.ui.xaml.data.h(1465,32):
          while compiling class template member function 'int32_t winrt::impl::produce<D,I>::Convert(void *,winrt::impl::struct_Windows_UI_Xaml_Interop_TypeName,void *,void *,void **) noexcept'
          with
          [
              D=WidgetColorConverter,
              I=winrt::Windows::UI::Xaml::Data::IValueConverter
          ]

“⟦Name⟧ is not a member of” is typical of a CRTP error, since the template is trying to call a method on the derived class, but it’s not there. Again, one could expect that people who encounter this error will understand what it means.

For the plain C++ case, you might have this:

// Plain C++ analogous scenario

struct WidgetColorConverter :
    OneWayConverter
{
    // Oops, forgot to implement Convert()
};

And everything works great until somebody tries to call the Convert method on a WidgetColorConverter and it’s not there.

test.cpp(79,20): error C2039: 'Convert': is not a member of 'WidgetColorConverter'

Again, this is a common error in C++ so you would hope that people understand what it means.

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.

But wait, we can do better. We’ll look at this some more next time.

Category

Topics

Author

Raymond Chen

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.