STL源码分析--全局内存处理函数
一、简介
stl 在
二、__uninitialized_default_n_a
// 调用空间分配器_Allocator提供的construct函数,填充[first, first + n)区间, 最终是调用默认构造函数。C++11及以上版本调用 template_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 _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 // 非标准指针类型的重载构造 templatestatic _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 static void construct(_Alloc& __a, pointer __p, const _Tp& __arg) { __a.construct(__p, __arg); } // 调用空间分配器_Allocator提供的construct函数,最终调的是拷贝构造 #endif // 以下是std::__alloc_traits的construct实现 // 普通模板类 template struct allocator_traits{} 的construct实现 templatestatic _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 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 struct allocator_traits template>{} 的construct实现 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 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 >{} 的construct实现 templatestatic _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_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_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
template
template
上述的cv限定,指const 和 volatile 限定。
Trival(平凡)类型的要求如下:
- 可平凡复制 (TriviallyCopyable)
- 拥有一个或多个默认构造函数,全部均为平凡或弃置(即设定为delete)的,且至少有一个未弃置。
可平凡复制的要求如下:
- 标量类型
- 可平凡复制类类型,即满足下列条件的类类型:
- 每个复制构造函数均为平凡或弃置的
- 每个移动构造函数均为平凡或弃置的
- 每个复制赋值运算符均为平凡或弃置的
- 每个移动赋值运算符均为平凡或弃置的
- 至少一个复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符未弃置
- 平凡而未弃置的析构函数
- 可平凡复制 (TriviallyCopyable) 对象的数组
这意味着可平凡复制类没有虚函数或虚基类。
以上来自C++ 具名要求:平凡类型 (TrivialType) - C++中文 - API参考文档 (apiref.com)
__uninitialized_default_n()内最终调用了__uninitialized_default_n_1
// 当模板参数为false时调用 template<bool _TrivialValueType> struct __uninitialized_default_n_1 { template_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 _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_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 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 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
template_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 ::__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 _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 _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 _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 _GLIBCXX20_CONSTEXPR inline typename __gnu_cxx::__enable_if::__value, _OutputIterator>::__type __fill_n_a1(_OutputIterator __first, _Size __n, const _Tp& __value) { for (; __n > 0; --__n, (void) ++__first) *__first = __value; return __first; } template _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 _GLIBCXX20_CONSTEXPR inline void __fill_a(_FIte __first, _FIte __last, const _Tp& __value) { std::__fill_a1(__first, __last, __value); } template void __fill_a(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>&, const ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>&, const _Tp&); template _GLIBCXX20_CONSTEXPR inline typename __gnu_cxx::__enable_if::__value, void>::__type __fill_a1(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { for (; __first != __last; ++__first) *__first = __value; } template _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 _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 char>(__tmp), __len); } template _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 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&);
三、__uninitialized_fill_n_a
// 调用空间分配器_Allocator提供的construct函数,填充[first, first + n)区间, 最终是调用拷贝构造函数 template_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 _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_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()的实现如下:
templateinline _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 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 () #else // C++98 可以不检查is_constructible , 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 { templatestatic _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 static _ForwardIterator __uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) { return std::fill_n(__first, __n, __x); } };
STL根据数据类型来决定初始化方式,当数据类型为trival(平凡)型且可赋值,采用赋值运算或memset来初始化数据成员,当数据类型为non-trival型或不可赋值,采用其拷贝构造函数来初始化数据成员。
四、__uninitialized_copy_a
// 普通版本,采用__traits::construct进行构造,将[first, first + n)区间数据拷贝到[__result, __result + n) template_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 _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_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()实现如下:
templateinline _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 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 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_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 _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 struct __copy_move<true, false, _Category> { template _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 _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 _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 _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
// 非move_iterator,__type为__false_type templatestruct __is_move_iterator { enum { __value = 0 }; typedef __false_type __type; }; // move_iterator,__type为__true_type template struct __is_move_iterator > { enum { __value = 1 }; typedef __true_type __type; };
template
// 判断两个迭代器类型是否能使用 memcpy/memmove. templatestruct __memcpyable { enum { __value = 0 }; }; // 迭代器为原生指针 template struct __memcpyable<_Tp*, _Tp*> : __is_nonvolatile_trivially_copyable<_Tp> { }; // 迭代器为原生指针 template struct __memcpyable<_Tp*, const _Tp*> : __is_nonvolatile_trivially_copyable<_Tp> { }; // A type that is safe for use with memcpy, memmove, memcmp etc. template 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 // and similar will be false. template struct __is_nonvolatile_trivially_copyable<volatile _Tp> { enum { __value = 0 }; };
五、__uninitialized_move_a
template_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
六、总结
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,或者不可拷贝、赋值,采用默认构造函数初始化数据;
2、__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,或者不可赋值,采用拷贝构造函数初始化数据;
3、__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,或不可拷贝构造,不可赋值,采用拷贝构造函数初始化数据;
4、__uninitialized_move_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _Allocator& __alloc)用来移动(C++11及以上)或拷贝[__first, __last)区间的数据到__result指向的地址空间,规则同__uninitialized_copy_a。