这几乎是正确的,但是编译器会提示你,函数模板在这个特化中无法使用。同时,如果我不提供终止函数,当参数包为空的时候,编译器将无法编译最终的调用。这不是函数重载的情况,因为参数列表依旧是相同的。幸运的是,解决方案非常简单。我可以通过提供一个无名的默认参数,来避免终止函数看起来像一个特化:
template <int = 0> void * FindInterface(GUID const &) noexcept { return nullptr; }编译器乐于此,同时,如果请求一个不支持的接口,终止函数会简单的返回一个空指针,同时虚函数QueryInterface将返回E_NOINTERFACE错误码。对IUnknown而言,这考虑得很周到。如果你所关心的是经典的COM,你可以安全的停在那里,因为那就是你所需要的所有内容。关于这点,可以反复的操作,编译器将优化QueryInterface的实现,其间使用各种各样的“递归的”函数调用和常量表达式,代码至少和你手工写的一样好。对于IInspectable而言,同样的方式也可以实现。
对于Windows 运行时类,实现IInspectable会增加额外的复杂度。这个接口并非是和IUnknown一样的基础性接口,它提供了一些不确定的工具的集合,而IUnknown则提供了绝对基础的函数。关于此,我会为以后的文章留下一个讨论,并聚焦于支持任意Windows运行时类的高效和现代的C++实现。首先,我会避开GetRuntimeClassName和GetTrustLevel虚函数。实现这两个方法相对而言微不足道,同时由于极少使用,所以它们的实现可以简单搪塞一下。GetRunTimeClassName方法,需要返回这个对象所代表的运行时类的完整名字的字符串。我将把这留给类自身去完成,决定是否去这样做。实现类模板可以简单地返回E_NOTIMPL,用来表明此方法并未实现:
HRESULT __stdcall GetRuntimeClassName(HSTRING * name) noexcept { *name = nullptr; return E_NOTIMPL; }同样的,GetTrustLevel 方法也会简单返回枚举类型的常量:
HRESULT __stdcall GetTrustLevel(TrustLevel * trustLevel) noexcept { *trustLevel = BaseTrust; return S_OK; }需要注意的是,我并没有显示的标记这些IInspectable方法为虚函数。避免声明为虚函数,是为了让编译器剔除这些函数,COM类无需真正的实现任何 IInspectable 接口。现在,我将注意力转移到IInspectable GetIids 方法。这比 QueryInterface 更容易产生错误。尽管它的实现不是那么严格,但是一个高效的编译器生成的实现也是可取的。GetIids 返回一个动态分配的 GUID 数组。每个 GUID 代表一个对象要实现的接口。起初你可能会认为,这只是对象通过 QueryInterface 所支持的一个简单的声明,但是那只在表面上看是正确的。GetIids 方法可能会保留一些接口而不公开。不管怎样,我会以基本定义作为开始:
HRESULT __stdcall GetIids(unsigned long * count, GUID ** array) noexcept { *count = 0; *array = nullptr;第一个参数指向调用者提供的变量,其中 GetIids 方法必须设置它为结果数组中的接口数目。第二个参数指向一个 GUID 数组,同时也表示着这里的实现是如何将动态分配的数组返回给调用者的。此处,我清除了这两者参数,只是为了安全起见。现在我需要决定这个类实现多少个接口。我想说的是,使用 sizeof 操作符,它可以确定这个参数包的大小,如下:
unsigned const size = sizeof ... (Interfaces);这相当方便,同时,编译器也会报告参数包拓展后要展现的模板参数的数目。这也是一个有效的常量表达式,它会在编译时产生一个值。我之前略为提及的,这无法实现的原因是,GetIids的实现会保留一些他们不愿和其他人共享的接口,这相当普遍。这些接口被称为隐含接口。任何人都可以通过QueryInterface来查询它们,但是GetIids不会告知这些接口是可用的。这样,我需要为排除了隐含接口的可变sizeof操作符,提供一个编译时的替代品。同时,我需要提供某种方式来声明和标识这些隐含接口。我以后者作为开始。对于组件开发人员,我希望让实现类变得尽可能简单,这样就需要一个不太引人注意的技巧。我简单的提供一个Cloaked类模板,用来“修饰”任意的隐含接口:
template <typename Interface> struct Cloaked : Interface {};