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
上述的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<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
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
// 非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. 总结
- __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,或者不可拷贝、赋值,采用默认构造函数初始化数据;
-
- __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,或者不可赋值,采用拷贝构造函数初始化数据;
-
- __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,或不可赋值,采用拷贝构造函数初始化数据;
-
- __uninitialized_move_a(_InputIterator __first, _InputIterator __last, _ForwardIterator __result, _Allocator& __alloc)用来移动(C++11及以上)或拷贝[__first, __last)区间的数据到__result指向的地址空间,规则同__uninitialized_copy_a。
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/16128186.html