IsInspectable 也是基于is_base_of特性的,但是当前适用于匹配接口。如果没找到基于IInspectable 的接口则终止:
template <int = 0> constexpr bool IsInspectable() noexcept { return false; }我会禁用掉稀奇古怪的默认参数。假定 IsInspectable 返回的是 true,我需要找到第一个IInspectable-based 接口:
template <int = 0> void * FindInspectable() noexcept { return nullptr; } template <typename First, typename ... Rest> void * FindInspectable() noexcept { // Find somehow }再次使用 is_base_of 特性,但这次要返回一个真实匹配的接口指针:
#pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant if (std::is_base_of<::IInspectable, First>::value) { return static_cast<First *>(this); } #pragma warning(pop) return FindInspectable<Rest ...>();BaseQueryInterface 这时可以利用IsInspectable 和 FindInspectable 一起来支持查询 IInspectable:
if (IsInspectable<Interfaces ...>() && id == __uuidof(::IInspectable)) { return FindInspectable<Interfaces ...>(); }然后指定具体的 Hen 类:
class Hen : public Implements<IHen, IHen2> { };实现类的模板,可以确保编译器能生成更高效的代码,不管 IHen、Hen2 来自 IInspectable 还是 IIUnknown (或者其他接口)。现在,我可以最后实现 QueryInterface 的递归部分,以及任何追加的接口,例如上面例子中的 IHen2。BaseQueryInterface 是靠调用 FindInterface 函数模板结束的:
template <typename First, typename ... Rest> void * BaseQueryInterface(GUID const & id) noexcept { if (id == __uuidof(First) || id == __uuidof(::IUnknown)) { return static_cast<First *>(this); } if (IsInspectable<Interfaces ...>() && id == __uuidof(::IInspectable)) { return FindInspectable<Interfaces ...>(); } return FindInterface<Rest ...>(id); }注意,我调用这个FindInterface函数模板,大致等同于我原来调用的BaseQueryInterface,在这个例子中,我向它传递接口的其余部分。我特意再次扩大参数包,这样它可以在列表的其余部分识别第一接口。但会提示一个故障。由于模板参数包不是以函数实参来扩展的,这可能会变得棘手,编程语言写不出来我想要的。更多的时候,这种“递归的”FindInterface可变模板正是你想要的:
template <typename First, typename ... Rest> void * FindInterface(GUID const & id) noexcept { if (id == __uuidof(First)) { return static_cast<First *>(this); } return FindInterface<Rest ...>(id); }它会从模板参数的其余部分中分离,如果有匹配就返回调整过的接口指针。另外,它也会调用自己,直到list取完。当我笼统地提及编译期递归时,重要的是要注意这个函数模板,以及其他类似的实现类模板的例子,在技术上递归,而不是在编译期。每个函数模板的实例调用不同的函数模板的实例。例如,FindInterface<IHen, IHen2> 调用 FindInterface<IHen2>, FindInterface<IHen2>调用 FindInterface<>。为了让它递归, FindInterface<IHen, IHen2>不需要调用FindInterface<IHen, IHen2>。
尽管如此,还是要记住,这样的“递归”发生在编译时,它就像你自己手写的一条条if语句。但是,现在,我遇到麻烦了。这个序列如何终止呢?当然是当模板参数列表为空的时候。这个问题在于C++已经定义了空模板参数列表的含义:
template <> void * FindInterface(GUID const &) noexcept { return nullptr; }