__vector_base_common 是怎么回事?

Hen*_*yF1 5 c++ libc++

我想看看 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
Run Code Online (Sandbox Code Playgroud)

实现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
Run Code Online (Sandbox Code Playgroud)

我有很多问题是如何制作矢量的..即使问一个也很尴尬..但是..为什么 __vector_base_common 采用 bool 模板参数?它似乎没有使用它,我验证了仅在代码中使用 is __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>
Run Code Online (Sandbox Code Playgroud)

私有与受保护之间的区别看起来...这是不需要那些抛出成员函数的向量的空间优化吗?我仍然有疑问为什么__vector_base_common需要模板参数。这个 C++ 模式有名字吗?

Seb*_*edl 6

这是一个实现技巧,因此库可以仅用作头文件,也可以具有预编译的部分。

的一些vector成员函数根本不依赖于模板参数;特别是抛出异常的辅助函数。因此可以(与依赖模板参数的部分不同)将它们编译一次并将它们放入共享库中。例如,这就是在 MacOS 上发生的情况。

另一方面,在库未随操作系统分发的平台上,如果用户不必分发共享库,而是将库用作仅标头,即包含<vector>和用它完成,而不必在构建中的链接器调用中添加标志。

这意味着您需要在头文件中提供这些函数的代码,但如果您使用共享库变体,则在使用头文件时实际上不应编译它。

这里介绍的技巧是实现这一目标的一种方法。首先,将实现放入模板中,这样它就可以存在于标题中而不会产生多个定义错误。有问题的模板只有一个虚拟参数;重要的是它是一个模板,而不是它有任何特定的参数。这是仅头文件库使用的常用技术。

现在您可以仅使用库头文件。但是如果你想使用共享库变体,你实际上需要提前编译代码并抑制库用户的代码生成。为此可以使用显式模板实例化。

因此,您在标题中放置了一个 extern 模板声明:

extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>;
Run Code Online (Sandbox Code Playgroud)

因此,标头现在包含显式专业化声明,禁止为模板成员生成代码。

然后您获取一个源文件,放入显式实例化,并将其编译为共享库。

template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common<true>;
Run Code Online (Sandbox Code Playgroud)

现在您已经涵盖了共享库的使用,但是您破坏了仅使用库头文件的能力。要恢复它,您需要extern template根据库的使用模式将声明设为可选。因此,您将声明包装在一个宏中,该宏的定义取决于模式:

_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
Run Code Online (Sandbox Code Playgroud)

这个宏是有条件地定义的

#ifdef _LIBCPP_DISABLE_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...)
#endif

#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
#endif
Run Code Online (Sandbox Code Playgroud)

因此,如果您处于仅标头模式(_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_commonas派生protected