如何强制派生类实现特定非虚方法(第一部分)
摘要
本文探讨了在C++中强制派生类实现特定非虚拟方法的技术,使用C++/WRL和C++/WinRT的示例,并批评了一个代码审查场景,其中仅使用注释而非适当强制。
<p>你可能有一个基类只实现了部分功能,并依赖派生类来完成其余部分。如何确保派生类完成剩余工作?</p>
<p>具体来说,假设我们正在实现 <code>IValueConverter</code>,它有两个方法:</p>
<ul>
<li><code>Convert()</code> 用于从源转换到目标。</li>
<li><code>ConvertBack()</code> 以便双向转换可以从目标转换回源。</li>
</ul>
<p>假设你想编写一个名为 <code>OneWayConverter</code> 的基类。它的实现会使 <code>ConvertBack()</code> 调用失败,并且你想强制派生类实现正向转换。</p>
<pre>// 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)
{
⟦ ... ⟧
}
};
</pre>
<p>在代码审查中,我看到有人只是通过写注释来尝试这样做。</p>
<pre>// 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");
}
};
</pre>
<p>我指出他们做得太多了。</p>
<p>强制某人在派生类中实现方法的方法,简单来说就是在基类中根本不实现该方法。</p>
<pre>// 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");
}
};
</pre>
<p>如果他们忘记实现它,错误消息取决于库。</p>
<pre>// C++/WRL
struct WidgetColorConverter :
Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRt>,
OneWayConverter>
{
// Oops, forgot to implement Convert()
};
</pre>
<p>错误在调用 <code>WRL::<wbr />Make</code> 尝试创建有缺陷的 <code>WidgetColorConverter</code> 时发生。</p>
<pre style="white-space: pre-wrap;">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::<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
windows.ui.xaml.data.h(2778,59):
see declaration of 'ABI::<wbr />Windows::<wbr />UI::<wbr />Xaml::<wbr />Data::<wbr />IValueConverter::<wbr />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::<wbr />WRL::<wbr />ComPtr<<wbr />WidgetColorConverter> Microsoft::<wbr />WRL::<wbr />Details::<wbr />Make<WidgetColorConverter,>(void)' being compiled
</pre>
<p>“由于以下成员无法实例化抽象类”是未能实现从基类继承的所有必要纯虚方法时的标准错误,因此可以期望遇到此错误的人会理解其含义。</p>
<pre>// C++/WinRT
struct WidgetColorConverter :
winrt::implements<WidgetColorConverter, winrt::Windows::UI::Xaml::Data::IValueConverter>,
OneWayConverter
{
// Oops, forgot to implement Convert()
};
</pre>
<p>错误在调用 <code>winrt::<wbr />make</code> 时发生
查看缓存全文
缓存时间: 2026/08/28 15:50
# 强制所有派生类实现特定非虚方法,第一部分——新旧事物解析
来源:https://devblogs.microsoft.com/oldnewthing/20260827-00?p=112651
你可能有一个只实现部分功能的基类,依赖于派生类来完成剩余工作。如何确保派生类完成剩余部分?为了具体说明,假设我们要实现 `IValueConverter` 接口,该接口包含两个方法:
- `Convert()` 用于将源数据转换为目标数据。
- `ConvertBack()` 使双向转换能从目标数据转换回源数据。
假设你想编写一个名为 `OneWayConverter` 的基类。它的实现会让 `ConvertBack()` 调用失败,并且你想强制派生类实现正向转换。
```
// C++/WRL
struct WidgetColorConverter : Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<1>,
ABI::Windows::UI::Xaml::Data::IValueConverter>,
OneWayConverter
{
// 要求派生类实现 Convert 方法
HRESULT STDMETHODCALLTYPE Convert(
IInspectable* value,
ABI::Windows::UI::Xaml::Interop::TypeName targetType,
IInspectable* parameter,
HSTRING language,
IInspectable** result) override
{
// ...
}
};
// C++/WinRT
struct WidgetColorConverter : winrt::implements<WidgetColorConverter, winrt::Windows::UI::Xaml::Data::IValueConverter>,
OneWayConverter
{
// 要求派生类实现 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)
{
// ...
}
};
// 纯C++类似场景
struct WidgetColorConverter : OneWayConverter
{
// 要求派生类实现 Convert 方法
Color Convert(Widget const& value)
{
// ...
}
};
```
在代码审查中,我看到有人仅通过编写注释来尝试实现这一点。
```
// C++/WRL
struct OneWayConverter
{
// 派生类必须重写此方法。
HRESULT STDMETHODCALLTYPE Convert(
IInspectable* /*value*/,
ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/,
IInspectable* /*parameter*/,
HSTRING /*language*/,
IInspectable** result) override
{
assert(false);
*result = nullptr;
return E_NOTIMPL;
}
// 单向转换器不能反向转换
HRESULT STDMETHODCALLTYPE ConvertBack(
IInspectable* /*value*/,
ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/,
IInspectable* /*parameter*/,
HSTRING /*language*/,
IInspectable** result) override
{
*result = nullptr;
return E_NOTIMPL;
}
};
// C++/WinRT
struct OneWayConverter
{
// 派生类必须重写此方法。
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();
}
// 单向转换器不能反向转换
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();
}
};
// 纯C++类似场景
struct OneWayConverter
{
// 派生类必须重写此方法。
Color Convert(Widget const& /*value*/)
{
assert(false);
throw std::exception("not implemented");
}
// 单向转换器不能反向转换
Widget ConvertBack(Color const& /*color*/)
{
throw std::exception("not implemented");
}
};
```
我指出他们工作做得过多。强制某人在派生类中实现方法的方法,就是根本不首先在基类中实现该方法。
```
// C++/WRL
struct OneWayConverter
{
// 派生类必须实现 Convert()
// 单向转换器不能反向转换
HRESULT STDMETHODCALLTYPE ConvertBack(
IInspectable* /*value*/,
ABI::Windows::UI::Xaml::Interop::TypeName /*targetType*/,
IInspectable* /*parameter*/,
HSTRING /*language*/,
IInspectable** result) override
{
*result = nullptr;
return E_NOTIMPL;
}
};
// C++/WinRT
struct OneWayConverter
{
// 派生类必须实现 Convert()
// 单向转换器不能反向转换
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();
}
};
// 纯C++类似场景
struct OneWayConverter
{
// 派生类必须实现 Convert()
// 单向转换器不能反向转换
Widget ConvertBack(Color const& /*color*/)
{
throw std::exception("not implemented");
}
};
```
如果他们忘记实现该方法,错误信息取决于具体的库。
```
// C++/WRL
struct WidgetColorConverter : Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<1>,
OneWayConverter>
{
// 糟糕,忘记实现 Convert() 了
};
```
错误发生在尝试调用 `WRL::Make` 创建有缺陷的 `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 Microsoft::WRL::Details::Make<WidgetColorConverter>(void)' being compiled
```
“由于以下成员无法实例化抽象类”是未能实现从基类继承的所有必要纯虚方法时的标准错误,因此可以预期遇到此错误的人会理解其含义。
```
// C++/WinRT
struct WidgetColorConverter : winrt::implements<WidgetColorConverter, winrt::Windows::UI::Xaml::Data::IValueConverter>,
OneWayConverter
{
// 糟糕,忘记实现 Convert() 了
};
```
错误发生在尝试调用 `winrt::make` 创建有缺陷的 `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<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter,winrt::Windows::UI::Xaml::Data::IValueConverter>' being compiled
winrt\base.h(6763,50): see reference to class template instantiation 'winrt::impl::producer_convert<winrt::Windows::Foundation::IInspectable,winrt::Windows::Foundation::IInspectable>' being compiled
winrt\base.h(6734,31): see reference to class template instantiation 'winrt::impl::producer<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter,winrt::implements<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter>>' being compiled
winrt\base.h(7137,23): see reference to class template instantiation 'winrt::impl::produce<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter>' being compiled
winrt\windows.ui.xaml.data.h(1465,32): while compiling class template member function 'int32_t winrt::impl::produce<WidgetColorConverter,winrt::Windows::UI::Xaml::Data::IValueConverter>::Convert(void *,winrt::impl::struct_Windows_UI_Xaml_Interop_TypeName,void *,void *,void **) noexcept'
with
[
D=WidgetColorConverter,
I=winrt::Windows::UI::Xaml::Data::IValueConverter
]
```
“⟦名称⟧ 不是成员”是 CRTP(奇异递归模板模式)错误的典型表现,因为模板试图调用派生类的方法,但该方法不存在。同样,可以预期遇到此错误的人会理解其含义。
对于纯C++的情况,你可能会遇到:
```
// 纯C++类似场景
struct WidgetColorConverter : OneWayConverter
{
// 糟糕,忘记实现 Convert() 了
};
```
一切都很正常,直到有人尝试调用 `WidgetColorConverter` 上的 `Convert` 方法,而该方法并不存在。
```
test.cpp(79,20): error C2039: 'Convert': is not a member of 'WidgetColorConverter'
```
同样,这是C++中常见的错误,因此希望人们能理解其含义。
很好,我们已经能够将所有这些编写错误转化为编译时错误,从而避免了有人使用基类却未能实现所有预期方法的风险。但是等等,我们可以做得更好。我们下次再深入探讨这个问题。
### 分类
### 主题
## 作者 Raymond Chen
Raymond 参与了Windows的演进超过30年。2003年,他创建了一个名为 The Old New Thing 的网站,其受欢迎程度远远超出了他的想象,这一发展至今仍让他感到不安。该网站催生了一本书,巧合的是也名为 The Old New Thing(Addison Wesley 2007年出版)。他偶尔会出现在 Windows Dev Docs Twitter 账户上,讲述一些不传达任何有用信息的故事。
相似文章
关于强制所有派生类实现特定非虚方法,第二部分
本文介绍了如何在C++中利用已删除的函数来强制派生类实现特定方法,以提升编译时错误提示并指导开发者。
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4
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.
在 C++/WinRT 中创建 Windows Runtime 委托的敏捷版本,第 9 部分
Raymond Chen 继续他的系列文章,讨论在 C++/WinRT 中创建敏捷版 Windows Runtime 委托,并比较 C++/WinRT、C++/CX 和 WRL 如何处理不可封送委托和敏捷引用创建。
在C++/WinRT中创建Windows Runtime委托的敏捷版本,第8部分
本文讨论了在C++/WinRT中创建敏捷委托时修复异常安全性问题,解决了未指定lambda捕获构造顺序导致的引用泄漏。
在C++/WinRT中创建Windows运行时代理的敏捷版本,第3部分
这篇博客文章讨论了在C++/WinRT中处理实现INoMarshal接口的Windows运行时代理,提供了一个敏捷的代理包装器,通过检查调用上下文来避免封送错误。