STL源码分析--全局内存处理函数

1. 简介

stl 在<bits/stl_uninitialized.h>文件中提供了一系列的全局内存处理函数,用于容器的初始化工作。此篇章主要分析该文件提供的部分函数。

2. __uninitialized_default_n_a

// 调用空间分配器_Allocator提供的construct函数,填充[first, first + n)区间, 最终是调用默认构造函数。C++11及以上版本调用
template<typename _ForwardIterator, typename _Size, typename _Allocator>
_GLIBCXX20_CONSTEXPR _ForwardIterator
__uninitialized_default_n_a(_ForwardIterator __first, _Size __n, _Allocator& __alloc)
{
    _ForwardIterator __cur = __first;
    __try
    {
        typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
        for (; __n > 0; --__n, (void) ++__cur)
            __traits::construct(__alloc, std::__addressof(*__cur));
        return __cur;
    }
    __catch(...)
    {
        std::_Destroy(__first, __cur, __alloc);
        __throw_exception_again;
    }
}

// 偏特化版本,空间分配器为std::new_allocator
template<typename _ForwardIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR inline _ForwardIterator
__uninitialized_default_n_a(_ForwardIterator __first, _Size __n, allocator<_Tp>&)
{ return std::__uninitialized_default_n(__first, __n); }

__uninitialized_default_n_a函数(C++11及其以上版本使用)提供了两个实现,一个为普通模板函数实现,一个为偏特化版本实现(空间分配器采用std::new_allocator)。

普通模板函数的实现内,__gnu_cxx::__alloc_traits为STL为空间分配器特制的萃取类,用于萃取空间分配器的相关特性。__traits::construct(__alloc, std::__addressof(*__cur))最终调用的是空间分配器__alloc的construct函数,用来对容器内数据成员进行构造处理(调用的是默认构造函数)。std::__addressof()对指定参数进行取地址操作。__uninitialized_default_n_a函数内进行for循环遍历,依次初始化[first, first + n)区间内的数据成员,同时函数内捕获异常,当构造函数抛出异常时,允许捕获并对整个区间内的数据成员进行析构处理(即std::_Destroy),回退异常前的构造。

__traits::construct()的实现细节如下

// 以下是__gnu_cxx::__alloc_traits的construct实现
#if __cplusplus >= 201103L
// 非标准指针类型的重载构造
template<typename _Ptr, typename... _Args>
static _GLIBCXX14_CONSTEXPR
std::__enable_if_t<__is_custom_pointer<_Ptr>::value>
construct(_Alloc& __a, _Ptr __p, _Args&&... __args)
noexcept(noexcept(_Base_type::construct(__a, std::__to_address(__p), std::forward<_Args>(__args)...)))
{
    // _Base_type为std::allocator_traits<_Alloc>
    _Base_type::construct(__a, std::__to_address(__p), std::forward<_Args>(__args)...);
}
#else
template<typename _Tp>
static void construct(_Alloc& __a, pointer __p, const _Tp& __arg)
{ __a.construct(__p, __arg); }    // 调用空间分配器_Allocator提供的construct函数,最终调的是拷贝构造
#endif

// 以下是std::__alloc_traits的construct实现

// 普通模板类 template<typename _Alloc> struct allocator_traits{} 的construct实现
template<typename _Tp, typename... _Args>
static _GLIBCXX20_CONSTEXPR auto
construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
noexcept(noexcept(_S_construct(__a, __p, std::forward<_Args>(__args)...)))
-> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...))
{ _S_construct(__a, __p, std::forward<_Args>(__args)...); }

template<typename _Tp, typename... _Args>
static _GLIBCXX14_CONSTEXPR _Require<__has_construct<_Tp, _Args...>>
_S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...)))
{ __a.construct(__p, std::forward<_Args>(__args)...); }


// 空间分配器为std::new_allocator,数据类型为_Tp的偏特化模板类
// template<typename _Tp> struct allocator_traits<allocator<_Tp>>{} 的construct实现
template<typename _Up, typename... _Args>
static _GLIBCXX20_CONSTEXPR void
construct(allocator_type& __a __attribute__((__unused__)), _Up* __p, _Args&&... __args)
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
{
#if __cplusplus <= 201703L
    __a.construct(__p, std::forward<_Args>(__args)...);
#else
    std::construct_at(__p, std::forward<_Args>(__args)...);
#endif
}

#if __cplusplus >= 202002L
template<typename _Tp, typename... _Args>
constexpr auto 
construct_at(_Tp* __location, _Args&&... __args)
noexcept(noexcept(::new((void*)0) _Tp(std::declval<_Args>()...)))
-> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
{ return ::new((void*)__location) _Tp(std::forward<_Args>(__args)...); }
#endif // C++20


// 空间分配器为std::new_allocator,数据类型为void的偏特化模板类
// template<> struct allocator_traits<allocator<void>>{} 的construct实现
template<typename _Up, typename... _Args>
static _GLIBCXX20_CONSTEXPR void
construct(allocator_type&, _Up* __p, _Args&&... __args)
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
{ std::_Construct(__p, std::forward<_Args>(__args)...); }

偏特化的版本调用的时std::__uninitialized_default_n()函数,实现如下:

// 填充 [first, first + n)区间内的数据,调用默认构造函数
template<typename _ForwardIterator, typename _Size>
_GLIBCXX20_CONSTEXPR inline _ForwardIterator
__uninitialized_default_n(_ForwardIterator __first, _Size __n)
{
    typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
    constexpr bool __can_fill = __and_<is_integral<_Size>, is_copy_assignable<_ValueType>>::value;

    return __uninitialized_default_n_1<__is_trivial(_ValueType) && __can_fill>::__uninit_default_n(__first, __n);
}

以上需关注的几个模板

template< class T > struct is_integral:检查 T 是否为整数类型。若 T 为 bool 、 char 、 char8_t 、 char16_t 、 char32_t 、 wchar_t 、 short 、 int 、 long 、 long long 类型,或任何实现定义的扩展整数类型,包含任何有符号、无符号及 cv 限定的变体。则提供等于 true 的成员常量 value 。否则, value 等于 false 。

template< class T > struct is_copy_assignable:用来判断一个类型是不是可拷贝赋值的。若 T 不是可引用类型(即可为 cv 限定的 void 或带 cv-qualifier-seq 或 ref-qualifier 的函数类型),则提供等于 false 的成员常量 value 。否则,提供等于 std::is_assignable<T&, const T&>::value 的成员常量 value 。

template<typename...> struct _and:多个模板参数的与逻辑判断,相当于&&,具备短路特性。_and<_B1, _B2>相当于B1::value && B2::value,当B1::value为true时返回B2::value,否则返回B1::value。

template struct is_trivial:若 T为平凡类型 (TrivialType) (即标量类型、有平凡默认构造函数的可平凡复制类,或这些类/类型的数组,可有 cv 限定),则提供等于 true 的成员常量value。对于任何其他类型,value为 false 。

上述的cv限定,指const 和 volatile 限定。

Trival(平凡)类型的要求如下

  • 可平凡复制 (TriviallyCopyable)
  • 拥有一个或多个默认构造函数,全部均为平凡或弃置(即设定为delete)的,且至少有一个未弃置。

可平凡复制的要求如下

  • 标量类型
  • 可平凡复制类类型,即满足下列条件的类类型:
    • 每个复制构造函数均为平凡或弃置的
    • 每个移动构造函数均为平凡或弃置的
    • 每个复制赋值运算符均为平凡或弃置的
    • 每个移动赋值运算符均为平凡或弃置的
    • 至少一个复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符未弃置
    • 平凡而未弃置的析构函数
  • 可平凡复制 (TriviallyCopyable) 对象的数组

这意味着可平凡复制类没有虚函数或虚基类。

以上来自 C++ 具名要求:平凡类型 (TrivialType) - C++中文 - API参考文档 (apiref.com)

__uninitialized_default_n()内最终调用了__uninitialized_default_n_1::__uninit_default_n()的实现

// 当模板参数为false时调用
template<bool _TrivialValueType>
struct __uninitialized_default_n_1
{
    template<typename _ForwardIterator, typename _Size>
    _GLIBCXX20_CONSTEXPR
    static _ForwardIterator __uninit_default_n(_ForwardIterator __first, _Size __n)
    {
        _ForwardIterator __cur = __first;
        __try
        {
            for (; __n > 0; --__n, (void) ++__cur)
              std::_Construct(std::__addressof(*__cur));
            return __cur;
        }
        __catch(...)
        {
            std::_Destroy(__first, __cur);
            __throw_exception_again;
        }
    }
};

// 偏特化实现,当模板参数为true时调用
template<>
struct __uninitialized_default_n_1<true>
{
    template<typename _ForwardIterator, typename _Size>
    _GLIBCXX20_CONSTEXPR
    static _ForwardIterator __uninit_default_n(_ForwardIterator __first, _Size __n)
    {
        if (__n > 0)
        {
            typename iterator_traits<_ForwardIterator>::value_type* __val = std::__addressof(*__first);
            std::_Construct(__val);
            ++__first;
            __first = std::fill_n(__first, __n - 1, *__val);
        }
        return __first;
    }
};

当 __can_fill为false或数据类型为non-trival,调用std::_Construct()函数完成[first, first + n) 区间内的数据项初始化,最终是通过placement new来完成构造初始化。std::_Construct()函数细节如下

// 通过调用placement new在已有内存上构造对象,调用的构造函数,可能是拷贝构造,也可能是默认构造
#if __cplusplus >= 201103L
template<typename _Tp, typename... _Args>
_GLIBCXX20_CONSTEXPR
inline void _Construct(_Tp* __p, _Args&&... __args)
{
#if __cplusplus >= 202002L
    // std::is_constant_evaluated()用来判断当前是不是常量计算,可用于constexpr函数判断是否是在编译期计算。
    if (std::__is_constant_evaluated())
    {
        std::construct_at(__p, std::forward<_Args>(__args)...);
        return;
    }
#endif
    ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
}
#else
template<typename _T1, typename _T2>
inline void _Construct(_T1* __p, const _T2& __value)
{
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    ::new(static_cast<void*>(__p)) _T1(__value);
}
#endif

// std::declval的功能:返回某个类型T的右值引用,不管该类型是否有默认构造函数或者该类型是否可以创建对象。
// 常配合decltype,令在decltype表达式中,不必经过该类型的构造函数就能使用该类型的成员函数。
// decltype用来做类型推导
#if __cplusplus >= 202002L
template<typename _Tp, typename... _Args>
constexpr auto construct_at(_Tp* __location, _Args&&... __args)
noexcept(noexcept(::new((void*)0) _Tp(std::declval<_Args>()...)))
-> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
{ return ::new((void*)__location) _Tp(std::forward<_Args>(__args)...); }
#endif // C++20

当__can_fill 为true且数据类型为trival,也就是__n为整数类型,数据类型为可赋值可构造时,调用的是template<> struct __uninitialized_default_n_1的实现,内部调用std::_Construct(),先构造一个数据项后,再以该项赋值给剩余的数据项(std::fill_n函数内实现,通过赋值运算符(自定义类型重载了赋值运算))。当类型为char、unsigned char、wchar_t时,std::fill_n是通过memset来完成数据项的初始化。std::fill_n函数实现细节如下

template<typename _OI, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline _OI fill_n(_OI __first, _Size __n, const _Tp& __value)
{
    __glibcxx_function_requires(_OutputIteratorConcept<_OI, const _Tp&>)

    return std::__fill_n_a(__first, std::__size_to_integer(__n), __value, std::__iterator_category(__first));
}

template<typename _Ite, typename _Seq, typename _Cat, typename _Size, typename _Tp>
::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>
__fill_n_a(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>& __first, 
    _Size __n, const _Tp& __value, std::input_iterator_tag);

template<typename _OutputIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline _OutputIterator __fill_n_a(_OutputIterator __first, _Size __n, const _Tp& __value, std::output_iterator_tag)
{
#if __cplusplus >= 201103L
    static_assert(is_integral<_Size>{}, "fill_n must pass integral size");
#endif
    return __fill_n_a1(__first, __n, __value);
}

template<typename _OutputIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline _OutputIterator __fill_n_a(_OutputIterator __first, _Size __n, const _Tp& __value, std::input_iterator_tag)
{
#if __cplusplus >= 201103L
    static_assert(is_integral<_Size>{}, "fill_n must pass integral size");
#endif
    return __fill_n_a1(__first, __n, __value);
}

template<typename _OutputIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline _OutputIterator __fill_n_a(_OutputIterator __first, _Size __n, const _Tp& __value, std::random_access_iterator_tag)
{
#if __cplusplus >= 201103L
    static_assert(is_integral<_Size>{}, "fill_n must pass integral size");
#endif
    if (__n <= 0)
        return __first;

    __glibcxx_requires_can_increment(__first, __n);

    std::__fill_a(__first, __first + __n, __value);
    return __first + __n;
}

template<typename _OutputIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline typename
__gnu_cxx::__enable_if<!__is_scalar<_Tp>::__value, _OutputIterator>::__type
__fill_n_a1(_OutputIterator __first, _Size __n, const _Tp& __value)
{
    for (; __n > 0; --__n, (void) ++__first)
        *__first = __value;
    return __first;
}

template<typename _OutputIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline typename
__gnu_cxx::__enable_if<__is_scalar<_Tp>::__value, _OutputIterator>::__type
__fill_n_a1(_OutputIterator __first, _Size __n, const _Tp& __value)
{
    const _Tp __tmp = __value;
    for (; __n > 0; --__n, (void) ++__first)
        *__first = __tmp;
    return __first;
}

template<typename _FIte, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline void __fill_a(_FIte __first, _FIte __last, const _Tp& __value)
{ std::__fill_a1(__first, __last, __value); }

template<typename _Ite, typename _Seq, typename _Cat, typename _Tp>
void __fill_a(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>&,
    const ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>&, const _Tp&);
    
template<typename _ForwardIterator, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline typename __gnu_cxx::__enable_if<!__is_scalar<_Tp>::__value, void>::__type
__fill_a1(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
{
    for (; __first != __last; ++__first)
        *__first = __value;
}

template<typename _ForwardIterator, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline typename __gnu_cxx::__enable_if<__is_scalar<_Tp>::__value, void>::__type
__fill_a1(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
{
    const _Tp __tmp = __value;
    for (; __first != __last; ++__first)
        *__first = __tmp;
}

// Specialization: for char types we can use memset.
template<typename _Tp>
_GLIBCXX20_CONSTEXPR
inline typename __gnu_cxx::__enable_if<__is_byte<_Tp>::__value, void>::__type
__fill_a1(_Tp* __first, _Tp* __last, const _Tp& __c)
{
    const _Tp __tmp = __c;
#if __cpp_lib_is_constant_evaluated
    if (std::is_constant_evaluated())
    {
        for (; __first != __last; ++__first)
            *__first = __tmp;
        return;
    }
#endif
    if (const size_t __len = __last - __first)
        __builtin_memset(__first, static_cast<unsigned char>(__tmp), __len);
}

template<typename _Ite, typename _Cont, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline void __fill_a1(::__gnu_cxx::__normal_iterator<_Ite, _Cont> __first,
::__gnu_cxx::__normal_iterator<_Ite, _Cont> __last, const _Tp& __value)
{ std::__fill_a1(__first.base(), __last.base(), __value); }

template<typename _Tp, typename _VTp>
void __fill_a1(const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>&,
    const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>&, const _VTp&);

_GLIBCXX20_CONSTEXPR
void __fill_a1(_GLIBCXX_STD_C::_Bit_iterator, _GLIBCXX_STD_C::_Bit_iterator, const bool&);

3. __uninitialized_fill_n_a

// 调用空间分配器_Allocator提供的construct函数,填充[first, first + n)区间, 最终是调用拷贝构造函数
template<typename _ForwardIterator, typename _Size, typename _Tp, typename _Allocator>
_GLIBCXX20_CONSTEXPR
_ForwardIterator __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, const _Tp& __x, _Allocator& __alloc)
{
    _ForwardIterator __cur = __first;
    __try
    {
        typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
        for (; __n > 0; --__n, (void) ++__cur)
            __traits::construct(__alloc, std::__addressof(*__cur), __x);
        return __cur;
    }
    __catch(...)
    {
        std::_Destroy(__first, __cur, __alloc);
        __throw_exception_again;
    }
}

// 偏特化版本,空间分配器为std::new_allocator
template<typename _ForwardIterator, typename _Size, typename _Tp, typename _Tp2>
_GLIBCXX20_CONSTEXPR
inline _ForwardIterator __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, const _Tp& __x, allocator<_Tp2>&)
{
#ifdef __cpp_lib_is_constant_evaluated
    if (std::is_constant_evaluated())
        return std::__do_uninit_fill_n(__first, __n, __x);
#endif
    return std::uninitialized_fill_n(__first, __n, __x);
}

__uninitialized_fill_n_a函数提供了两个实现,一个为普通模板函数实现,一个为偏特化版本实现(空间分配器采用std::new_allocator)。类似于__uninitialized_default_n_a,区别在于前者最终调用的构造函数是拷贝构造而不是默认构造。

当__uninitialized_fill_n_a函数的调用处于常量表达式计算中,即 std::is_constant_evaluated()为true,则调用std::__do_uninit_fill_n(__first, __n, __x)完成[first, first + n) 区间内的数据项初始化,否则使用std::uninitialized_fill_n(__first, __n, __x)。

std::__do_uninit_fill_n()的实现如下:

template<typename _ForwardIterator, typename _Size, typename _Tp>
_GLIBCXX20_CONSTEXPR
_ForwardIterator
__do_uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
{
    _ForwardIterator __cur = __first;
    __try
    {
        for (; __n > 0; --__n, (void) ++__cur)
            std::_Construct(std::__addressof(*__cur), __x);
        return __cur;
    }
    __catch(...)
    {
        std::_Destroy(__first, __cur);
        __throw_exception_again;
    }
}

std::uninitialized_fill_n()的实现如下:

template<typename _ForwardIterator, typename _Size, typename _Tp>
inline _ForwardIterator uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
{
    typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;

    // Trivial类型不使用构造函数来做初始化,所以使用std::fill_n
    // 对于任意类类型和浮点类型,我们不能假设_n>0和std:_size_to_integer(_n)>0是等效的,
    // 所以只有在_size已经是整数类型时才使用std::fill_n。
    const bool __can_fill = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType, const _Tp&) && __is_integer<_Size>::__value;
    return __uninitialized_fill_n<__can_fill>::__uninit_fill_n(__first, __n, __x);
}

#if __cplusplus >= 201103L
template<typename _ValueType, typename _Tp>
constexpr bool __check_constructible()
{
    // Trivial类型可以将构造函数设成delete,但是像std::copy这些仅使用赋值(或memmove)而不使用构造的函数,
    // 我们仍需检查类型_Tp的构造函数是否有效,否则std::uninitialized_xxx内的一些格式不正确的用法将能编译通过而不报错
    static_assert(is_constructible<_ValueType, _Tp>::value,
    "result type must be constructible from input type");
    return true;
}

// 如果是Trivial类型则可以不必构造,只需赋值即可。但Trivial类型仍然可以将赋值运算重载函数设置为delete或不可访问。
// 所以如果不能赋值,就不要尝试使用std::copy和std::fill。
# define _GLIBCXX_USE_ASSIGN_FOR_INIT(T, U) \
    __is_trivial(T) && __is_assignable(T&, U) \
    && std::__check_constructible<T, U>()
#else
// C++98 可以不检查is_constructible<T, U>, Trivial类型没有用户声明的构造函数,
// 所以如果可以赋值的话,那么构造也应当可以
# define _GLIBCXX_USE_ASSIGN_FOR_INIT(T, U) \
    __is_trivial(T) && __is_assignable(T&, U)
#endif
// 当模板参数为false时调用, 即数据类型为non-trival类型或不可赋值
template<bool _TrivialValueType>
struct __uninitialized_fill_n
{
    template<typename _ForwardIterator, typename _Size, typename _Tp>
    static _ForwardIterator __uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
    { return std::__do_uninit_fill_n(__first, __n, __x); }
};

// 偏特化实现,当模板参数为true时调用, 即数据类型为trival类型且可赋值
template<>
struct __uninitialized_fill_n<true>
{
    template<typename _ForwardIterator, typename _Size, typename _Tp>
    static _ForwardIterator __uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
    { return std::fill_n(__first, __n, __x); }
};

STL根据数据类型来决定初始化方式,当数据类型为trival(平凡)型且可赋值,采用赋值运算或memset来初始化数据成员,当数据类型为non-trival型或不可赋值,采用其拷贝构造函数来初始化数据成员。

4. __uninitialized_copy_a

// 普通版本,采用__traits::construct进行构造,将[first, first + n)区间数据拷贝到[__result, __result + n)
template<typename _InputIterator, typename _ForwardIterator, typename _Allocator>
_GLIBCXX20_CONSTEXPR
_ForwardIterator __uninitialized_copy_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _Allocator& __alloc)
{
    _ForwardIterator __cur = __result;
    __try
    {
        typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
        for (; __first != __last; ++__first, (void)++__cur)
            __traits::construct(__alloc, std::__addressof(*__cur), *__first);
        return __cur;
    }
    __catch(...)
    {
        std::_Destroy(__result, __cur, __alloc);
        __throw_exception_again;
    }
}

// 偏特化版本,空间分配器为std::new_allocator
template<typename _InputIterator, typename _ForwardIterator, typename _Tp>
_GLIBCXX20_CONSTEXPR
inline _ForwardIterator __uninitialized_copy_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, allocator<_Tp>&)
{
#ifdef __cpp_lib_is_constant_evaluated
    if (std::is_constant_evaluated())
        return std::__do_uninit_copy(__first, __last, __result);
#endif
    return std::uninitialized_copy(__first, __last, __result);
}

__uninitialized_copy_a函数提供了两个实现,一个为普通模板函数实现,一个为偏特化版本实现(空间分配器采用std::new_allocator)。

普通函数模板实现,采用__traits::construct(__alloc, std::__addressof(*__cur), __first),最终调用指定的空间分配器提供的construct,调用数据类型的拷贝构造函数,将first 拷贝到__cur所指空间。

偏特化版本实现,若__uninitialized_copy_a函数在常量计算中使用,即std::is_constant_evaluated()为true,则调用std::__do_uninit_copy()进行数据项拷贝,否则,调用std::uninitialized_copy()。

std::__do_uninit_copy()实现如下:

// 使用std::_Construct()进行数据项初始化,最终用的是拷贝构造函数
template<typename _InputIterator, typename _ForwardIterator>
_GLIBCXX20_CONSTEXPR
_ForwardIterator __do_uninit_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result)
{
    _ForwardIterator __cur = __result;
    __try
    {
        for (; __first != __last; ++__first, (void)++__cur)
            std::_Construct(std::__addressof(*__cur), *__first);
        return __cur;
    }
    __catch(...)
    {
        std::_Destroy(__result, __cur);
        __throw_exception_again;
    }
}

std::uninitialized_copy()实现如下:

template<typename _InputIterator, typename _ForwardIterator>
inline _ForwardIterator uninitialized_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result)
{
    typedef typename iterator_traits<_InputIterator>::value_type _ValueType1;
    typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType2;

    // _ValueType1 必须是trivially-copyable 才可以使用memmove
    const bool __can_memmove = __is_trivial(_ValueType1);

#if __cplusplus < 201103L
    typedef typename iterator_traits<_InputIterator>::reference _From;
#else
    using _From = decltype(*__first);
#endif
    const bool __assignable = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType2, _From);
    return std::__uninitialized_copy<__can_memmove && __assignable>::__uninit_copy(__first, __last, __result);
}

// 若数据类型不支持赋值,则使用此版本,使用std::__do_uninit_copy,最终用拷贝构造函数进行数据项初始化
template<bool _TrivialValueTypes>
struct __uninitialized_copy
{
    template<typename _InputIterator, typename _ForwardIterator>
    static _ForwardIterator __uninit_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result)
    { return std::__do_uninit_copy(__first, __last, __result); }
};

// 若数据类型支持拷贝赋值,则使用此版本,最终用std::copy()进行数据项拷贝
template<>
struct __uninitialized_copy<true>
{
    template<typename _InputIterator, typename _ForwardIterator>
    static _ForwardIterator __uninit_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result)
    { return std::copy(__first, __last, __result); }
};

std::copy()会根据迭代器的类型,判断采用哪种拷贝策略。当迭代器为move_iterator,支持移动语义,则使用std::move(),将数据项转为右值引用,最终调用数据类型的移动赋值运算符进行数据项拷贝(C++11及以上版本)。当迭代器为原生指针且数据类型不为volatile,可平凡拷贝(trivially_copyable)时,采用memmove进行数据项拷贝。否则,采用重载赋值运算符进行数据项拷贝。具体实现细节如下:

template<typename _II, typename _OI>
_GLIBCXX20_CONSTEXPR
inline _OI copy(_II __first, _II __last, _OI __result)
{
    // concept requirements
    __glibcxx_function_requires(_InputIteratorConcept<_II>)
    __glibcxx_function_requires(_OutputIteratorConcept<_OI, typename iterator_traits<_II>::reference>)
    __glibcxx_requires_can_increment_range(__first, __last, __result);

    return std::__copy_move_a<__is_move_iterator<_II>::__value>(std::__miter_base(__first), std::__miter_base(__last), __result);
}

template<bool _IsMove, typename _II, typename _OI>
_GLIBCXX20_CONSTEXPR
inline _OI __copy_move_a(_II __first, _II __last, _OI __result)
{
    return std::__niter_wrap(__result, std::__copy_move_a1<_IsMove>(std::__niter_base(__first), 
        std::__niter_base(__last), std::__niter_base(__result)));
}

template<bool _IsMove, typename _II, typename _OI>
_GLIBCXX20_CONSTEXPR
inline _OI __copy_move_a1(_II __first, _II __last, _OI __result)
{ return std::__copy_move_a2<_IsMove>(__first, __last, __result); }

template<bool _IsMove, typename _II, typename _OI>
_GLIBCXX20_CONSTEXPR
inline _OI __copy_move_a2(_II __first, _II __last, _OI __result)
{
    typedef typename iterator_traits<_II>::iterator_category _Category;
#ifdef __cpp_lib_is_constant_evaluated
    if (std::is_constant_evaluated())
        return std::__copy_move<_IsMove, false, _Category>::__copy_m(__first, __last, __result);
#endif
    return std::__copy_move<_IsMove, __memcpyable<_OI, _II>::__value, _Category>::__copy_m(__first, __last, __result);
}

// 默认采用赋值运算来做拷贝
template<bool _IsMove, bool _IsSimple, typename _Category>
struct __copy_move
{
    template<typename _II, typename _OI>
    _GLIBCXX20_CONSTEXPR
    static _OI __copy_m(_II __first, _II __last, _OI __result)
    {
        for (; __first != __last; ++__result, (void)++__first)
            *__result = *__first;
        return __result;
    }
};

// 支持移动构造语义,非原生指针,不可memcpy和memmove
#if __cplusplus >= 201103L
template<typename _Category>
struct __copy_move<true, false, _Category>
{
    template<typename _II, typename _OI>
    _GLIBCXX20_CONSTEXPR
    static _OI __copy_m(_II __first, _II __last, _OI __result)
    {
        for (; __first != __last; ++__result, (void)++__first)
            *__result = std::move(*__first);
        return __result;
    }
};
#endif

// 不支持移动构造,非原生指针,不可memcpy和memmove
template<>
struct __copy_move<false, false, random_access_iterator_tag>
{
    template<typename _II, typename _OI>
    _GLIBCXX20_CONSTEXPR
    static _OI __copy_m(_II __first, _II __last, _OI __result)
    {
        typedef typename iterator_traits<_II>::difference_type _Distance;
        for(_Distance __n = __last - __first; __n > 0; --__n)
        {
            *__result = *__first;
            ++__first;
            ++__result;
        }
        return __result;
    }
};

// 支持移动构造语义,非原生指针,不可memcpy和memmove
#if __cplusplus >= 201103L
template<>
struct __copy_move<true, false, random_access_iterator_tag>
{
    template<typename _II, typename _OI>
    _GLIBCXX20_CONSTEXPR
    static _OI __copy_m(_II __first, _II __last, _OI __result)
    {
        typedef typename iterator_traits<_II>::difference_type _Distance;
        for(_Distance __n = __last - __first; __n > 0; --__n)
        {
            *__result = std::move(*__first);
            ++__first;
            ++__result;
        }
        return __result;
    }
};
#endif

// 原生指针,且数据类型不为volatile,满足trivially_copyable,则使用memmove
template<bool _IsMove>
struct __copy_move<_IsMove, true, random_access_iterator_tag>
{
    template<typename _Tp>
    _GLIBCXX20_CONSTEXPR
    static _Tp* __copy_m(const _Tp* __first, const _Tp* __last, _Tp* __result)
    {
#if __cplusplus >= 201103L
        using __assignable = __conditional_t<_IsMove, is_move_assignable<_Tp>, is_copy_assignable<_Tp>>;
        // trivial types can have deleted assignment
        static_assert( __assignable::value, "type must be assignable" );
#endif
        const ptrdiff_t _Num = __last - __first;
        if (_Num)
            __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
        return __result + _Num;
    }
};

其中,template struct __is_move_iterator{}模板用来判断迭代器是否为move_iterator。

// 非move_iterator,__type为__false_type
template<typename _Tp>
struct __is_move_iterator
{
    enum { __value = 0 };
    typedef __false_type __type;
};

// move_iterator,__type为__true_type
template<typename _Iterator>
struct __is_move_iterator<move_iterator<_Iterator> >
{
    enum { __value = 1 };
    typedef __true_type __type;
};

template<typename _OutputIter, typename _InputIter> struct __memcpyable{}模板用来判断迭代器是否能使用memmove和memcpy等内存操作函数

// 判断两个迭代器类型是否能使用 memcpy/memmove.
template<typename _OutputIter, typename _InputIter>
struct __memcpyable
{
    enum { __value = 0 };
};

// 迭代器为原生指针
template<typename _Tp>
struct __memcpyable<_Tp*, _Tp*>
: __is_nonvolatile_trivially_copyable<_Tp>
{ };

// 迭代器为原生指针
template<typename _Tp>
struct __memcpyable<_Tp*, const _Tp*>
: __is_nonvolatile_trivially_copyable<_Tp>
{ };

// A type that is safe for use with memcpy, memmove, memcmp etc.
template<typename _Tp>
struct __is_nonvolatile_trivially_copyable
{
    enum { __value = __is_trivially_copyable(_Tp) };
};

// Cannot use memcpy/memmove/memcmp on volatile types even if they are
// trivially copyable, so ensure __memcpyable<volatile int*, volatile int*>
// and similar will be false.
template<typename _Tp>
struct __is_nonvolatile_trivially_copyable<volatile _Tp>
{
    enum { __value = 0 };
};

5. __uninitialized_move_a

template<typename _InputIterator, typename _ForwardIterator, typename _Allocator>
_GLIBCXX20_CONSTEXPR
inline _ForwardIterator __uninitialized_move_a(_InputIterator __first, _InputIterator __last, 
    _ForwardIterator __result, _Allocator& __alloc)
{
    return std::__uninitialized_copy_a(_GLIBCXX_MAKE_MOVE_ITERATOR(__first), 
        _GLIBCXX_MAKE_MOVE_ITERATOR(__last), __result, __alloc);
}

__uninitialized_move_a 函数调用__uninitialized_copy_a()实现将[__first, last)的数据移动到__result起始的地址空间上。C++11及以上版本,通过_GLIBCXX_MAKE_MOVE_ITERATOR宏将迭代器转化为move_iter,从而使__uninitialized_copy_a()底层调用到std::move,实现移动赋值,而原[__first, last)的数据将不再可用。C++11以下,_GLIBCXX_MAKE_MOVE_ITERATOR宏为空,迭代器不做任何转化,最终实现的是拷贝而不是移动。

#if __cplusplus >= 201103L
#define _GLIBCXX_MAKE_MOVE_ITERATOR(_Iter) std::make_move_iterator(_Iter)
#define _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(_Iter) \
  std::__make_move_if_noexcept_iterator(_Iter)
#else
#define _GLIBCXX_MAKE_MOVE_ITERATOR(_Iter) (_Iter)
#define _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(_Iter) (_Iter)
#endif // C++11

6. 总结

  1. __uninitialized_default_n_a(ForwardIterator __first, _Size __n, _Allocator& __alloc)用来初始化[__first, __first + n)区间的数据,初始化原则如下:
  • 当空间分配器非std::new_allocator,一律采用默认构造函数初始化数据

  • 当空间分配器为std::nwe_allocator

    • 当数据类型为trival,且可拷贝,可赋值,非char及各种char的变体类型,采用重载的赋值运算符初始化数据

    • 当数据类型为char型及各种char的变体类型,采用memset初始化数据;

    • 当数据类型为non-trival,或者不可拷贝、赋值,采用默认构造函数初始化数据;

  1. __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, const _Tp& __x, _Allocator& __alloc)用来初始化[__first, __first + n)区间的数据,初始化原则如下:
  • 当空间分配器非std::new_allocator,一律采用拷贝构造函数初始化数据;

  • 当空间分配器为std::nwe_allocator

    • 当数据类型为trival,且可拷贝构造,可赋值,非char及各种char的变体类型,采用重载的赋值运算符初始化数据;

    • 当数据类型为char型及各种char的变体类型,采用memset初始化数据;

    • 当数据类型为non-trival,或者不可赋值,采用拷贝构造函数初始化数据;

  1. __uninitialized_copy_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _Allocator& __alloc)用来拷贝[__first, __last)区间的数据到__result指向的地址空间,规则如下:
  • 当空间分配器非std::new_allocator,一律采用拷贝构造函数初始化数据;

  • 当空间分配器为std::nwe_allocator

    • 当源数据类型为trival,且可拷贝,目标数据类型为trival,且可构造,可赋值,迭代器类型非原生指针、非move_iterator,采用重载的赋值运算符拷贝数据;

    • 当源数据类型为trival,且可拷贝,目标数据类型为trival,且可构造,可赋值,迭代器类型为move_iterator(编译器至少要支持C++11),采用重载的移动赋值运算符拷贝数据;

    • 当源数据类型为trival,且可拷贝,目标数据类型为trival,且可构造,可赋值,迭代器类型为原生指针,且数据类型非volatile,采用memmove拷贝数据;

    • 当源数据类型为non-trival,或不可拷贝,或目标数据类型为non-trival,或不可赋值,采用拷贝构造函数初始化数据;

  1. __uninitialized_move_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _Allocator& __alloc)用来移动(C++11及以上)或拷贝[__first, __last)区间的数据到__result指向的地址空间,规则同__uninitialized_copy_a。
posted @ 2022-04-16 21:38  流翎  阅读(294)  评论(0编辑  收藏  举报