c++ - __vector_base_common 发生了什么?
问题描述
我想看看 C++ 向量是如何制作的。我发现了这个,实现是 LLVM 编译器https://llvm.org/svn/llvm-project/libcxx/trunk/src/vector.cpp appleclang
src/vector.cpp:
#include "vector"
_LIBCPP_BEGIN_NAMESPACE_STD
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common<true>;
_LIBCPP_END_NAMESPACE_STD
实现https://llvm.org/svn/llvm-project/libcxx/trunk/include/vector appleclang LLVM。
包括/向量:
// .. deleted code
template <bool>
class __vector_base_common
{
protected:
_LIBCPP_ALWAYS_INLINE __vector_base_common() {}
_LIBCPP_NORETURN void __throw_length_error() const;
_LIBCPP_NORETURN void __throw_out_of_range() const;
};
template <bool __b>
void
__vector_base_common<__b>::__throw_length_error() const
{
_VSTD::__throw_length_error("vector");
}
template <bool __b>
void
__vector_base_common<__b>::__throw_out_of_range() const
{
_VSTD::__throw_out_of_range("vector");
}
_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
// .. deleted code
template <class _Tp, class _Allocator>
class __vector_base
: protected __vector_base_common<true>
// .. deleted code
class _LIBCPP_TEMPLATE_VIS vector
: private __vector_base<_Tp, _Allocator>
// .. deleted code
我有很多问题向量是如何制作的。即使问一个向量都感觉很尴尬。但是..为什么__vector_base_common 采用bool 模板参数?它似乎没有使用它,并且我验证了仅在代码中使用的是__vector_base_common<true>
,没有使用该false
值。
编辑:有很多与vector<bool>
此相关的建议。仅使用上述 bool 参数的一种特化 ( true
)。这就是vector special的样子
template <class _Allocator>
class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator>
: private __vector_base_common<true>
私有与受保护之间的区别......这是不需要那些抛出成员函数的向量的空间优化吗?我仍然有疑问为什么__vector_base_common
需要模板参数。这个 C++ 模式有名字吗?
解决方案
这是一种实现技巧,因此该库可以仅用作标头,也可以具有预编译部分。
的一些vector
成员函数根本不依赖于模板参数;特别是抛出异常的辅助函数。因此可以(与依赖模板参数的部分不同)编译一次并将它们放入共享库中。例如,这就是在 MacOS 上发生的事情。
另一方面,在库不随操作系统分发的平台上,如果用户不必分发共享库,而是可以将库用作仅头文件,即包含<vector>
和完成它,而不必在构建中向链接器调用添加标志。
这意味着您需要这些函数的代码在头文件中可用,但是如果您使用共享库变体,则在使用头文件时它实际上不应该被编译。
这里介绍的技巧是实现这一目标的一种方法。首先,将实现放入模板中,以便它可以存在于标头中而不会产生多个定义错误。有问题的模板只有一个虚拟参数;重要的是它是一个模板,而不是它有任何特定的参数。这是仅头文件库使用的常用技术。
现在您可以仅使用库头文件。但是如果你想使用共享库变体,你实际上需要提前编译代码并为库用户抑制代码生成。为此可以使用显式模板实例化。
所以你在标题中放置了一个外部模板声明:
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>;
所以标题现在包含一个显式的特化声明,禁止模板成员的代码生成。
然后您获取一个源文件,放入显式实例化,并将其编译为共享库。
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common<true>;
现在您已经涵盖了共享库的使用,但是您破坏了仅使用库头文件的能力。要恢复它,您需要extern template
根据库的使用模式将声明设为可选。因此,您将声明包装在一个宏中,该宏的定义取决于模式:
_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
这个宏是有条件地定义的:
#ifdef _LIBCPP_DISABLE_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...)
#endif
#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
#endif
因此,如果您处于仅标头模式(_LIBCPP_DISABLE_EXTERN_TEMPLATE
已定义),则声明将消失。如果您处于共享库模式,则声明在那里,防止代码生成。
vector<bool>
私有派生的原因__vector_base_common
是因为它本身没有任何需要访问 throw 帮助器的派生类。vector<T>
源自__vector_base<T>
,__vector_base<T>
又源自__vector_base_common
; 因此,vector<T>
要访问__vector_base_common
成员,__vector_base<T>
必须从__vector_base_common
as派生protected
。
推荐阅读
- node.js - NodeJS:如何在不暴露证书内容的情况下安全地提供服务
- github - 没有标签的预提交挂钩?
- reactjs - 反应路由器
不能正常工作 - firebase - 如何在 Flutter 中滚动到底部工作表?
- git - 为不太懂技术的人编写 Git/GitHub 操作脚本
- python - Python - 应用程序和操作系统权限
- android - 如何禁用 dynatrace 以调试构建 android
- r - likert 包中的绘图问题 - 图像中的多余行
- python-3.x - 是否有一个 FastAPI 库可用于将端点标记为受保护并验证仅 HTTP Cookie 中的 Auth JWT 令牌?
- graphql - gql 和 buildSchema 有什么区别?