C草的编译期编程:元模版
最近一直在写time-devourer这个项目,我的多态设计的强迫症又犯了,不可避免和元模版打交道了。这篇文章简单的稍微讲一下我遇到的几个场景。
- 自动包装COM对象指针,生命周期结束自动调用Release。要求T必须能调用Release方法,且T必须是IUnknown的子类。
头一次写模版元编程给我肘晕了,这就是编译期编程么,害怕.
这里稍微总结一下几个点,最上面的is_com_interface有两个模版参数,
第一个是T, 第二个是void, void没什么意义,主要用来做特化匹配.
下面是重点,typename T只有一个参数,默认外部调用的都是这个模板,例如COMPtr
std::void_t<条件…> 这个条件如果成立,就会变成void, 就能用上这个模版了
第一个条件:decltype(std::declval
std::declvar
decltype是一个类型提取器,返回值就是一个Type,运行方式sizeof很像,只能在编译期运行,不能在运行时调用。
如果declval模拟的对象的Release方法调用失败了,那就没有返回值,decltype就会报错,就无法匹配到这个模版了。
第二个条件:std::enable_if_t<std::is_base_of_v<IUnknown, T»
这个简单一些,判断T是否是IUnknown的子类,是就enable_if_t
不是就enable_if_t
template <typename T, typename = void>
struct is_com_interface : std::false_type
{
};
template <typename T>
struct is_com_interface<
T,
std::void_t<
decltype(std::declval<T>().Release()),
std::enable_if_t<std::is_base_of_v<IUnknown, T>>
>
> : std::true_type
{
};
template <typename T>
class COMPtr
{
static_assert(is_com_interface<T>::value, "Type is not a COM object");
T* com_ptr;
public:
COMPtr() : com_ptr(nullptr)
{
}
COMPtr(std::nullptr_t) : com_ptr(nullptr)
{
}
COMPtr(T* p) : com_ptr(p)
{
}
~COMPtr()
{
if (com_ptr) com_ptr->Release();
};
explicit operator bool() const { return com_ptr != nullptr; }
T* Get() { return com_ptr; }
T* operator->() { return com_ptr; }
T** GetAddressOf()
{
if (com_ptr)
{
com_ptr->Release();
}
com_ptr = nullptr;
return &com_ptr;
}
};
写了C++才发现,Rust这个语言本身很多的原语设计都是沿袭了C++的设计。
生命周期,智能指针,这些概念C++本身也有,只是并不强迫你使用。Rust只是做的更加激进罢了。
也许是Win32编程的缘故,我遇到需要显式分配和释放内存的场景很少,如果有也能封装成RAII的方式来管理资源。
或许需要显式的手动分配和释放内存的时代早就结束了…