背景:
最近看了候捷老师的c++的教程,他说移动构造方法要加noexcept,在vector扩容的时候,如果有移动构造方法没有加noexcept,是不会调用的.个人感觉有些神奇,这就去查下一探究竟.
过程:
测试代码如下:
#include
#include
struct A
{
A(){
std::cout vecA;
A a;
vecA.push_back(a);
std::cout
执行结果如下:
A::A()
A::A(const A&a)
1
A::A(const A&a)
A::A(const A&a)
2
A::A(const A&a)
A::A(const A&a)
A::A(const A&a)
3
A::A(const A&a)
4
我们知道vector是要扩容的,在A(A&&a)并没有添加noexcept关键字,所以扩容的时候,使用的也是拷贝构造方法,那接下来我们看下加下noexcept后了,结果是什么样的
#include
#include
struct A
{
A(){
std::cout vecA;
A a;
vecA.push_back(a);
std::cout
执行结果如下:
A::A()
A::A(const A&a)
1
A::A(const A&a)
A::A(A &&a)
2
A::A(const A&a)
A::A(A &&a)
A::A(A &&a)
3
A::A(const A&a)
4
在A(A&&a)noexcept后,调用的方法就是移动构造方法,感觉挺不可思议的,带着这个疑问,我们看下std::vector源码来找寻答案
揭秘:
push_back源码如下:
template
inline _LIBCPP_INLINE_VISIBILITY
void
vector::push_back(const_reference __x)
{
if (this->__end_ != this->__end_cap())
{
__RAII_IncreaseAnnotator __annotator(*this);
__alloc_traits::construct(this->__alloc(),
_VSTD::__to_raw_pointer(this->服务器托管网;__end_), __x);
__annotator.__done();
++this->__end_;
}
else
__push_back_slow_path(__x);
}
因为我们要看扩容相关的代码,__push_back_slow_path(__x);对应的需要扩容要调用的代码
#ifndef _LIBCPP_CXX03_LANG
vector::__push_back_slow_path(_Up&& __x)
#else
vector::__push_back_slow_path(_Up& __x)
#endif
{
allocator_type& __a = this->__alloc();
__split_buffer __v(__recommend(size() + 1), size(), __a);
// __v.push_back(_VSTD::forward(__x));
__alloc_traits::construct(__a, _VSTD::__to_raw_pointer(__v.__end_), _VSTD::forward(__x));
__v.__end_++;
__swap_out_circular_buffer(__v);
}
上边是分配内从,我们重点看下__swap_out_circular_buffer(__v);把老的元素拷贝新的申请区域上
template
void
vector::__swap_out_circular_buffer(__split_buffer& __v)
{
__annotate_delete();
__alloc_traits::__construct_backward(this->__alloc(), this->__begin_, this->__end_, __v.__begin_);
_VSTD::swap(this->__begin_, __v.__begin_);
_VSTD::swap(this->__end_, __v.__end_);
_VSTD::swap(this->__end_cap(), __v.__end_cap());
__v.__first_ = __v.__begin_;
__annotate_new(size());
__invalidate_all_服务器托管网iterators();
}
在看下__alloc_traits::__construct_backward这块代码
template
_LIBCPP_INLINE_VISIBILITY
static
void
__construct_backward(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2)
{
while (__end1 != __begin1)
{
construct(__a, _VSTD::__to_raw_pointer(__end2-1), _VSTD::move_if_noexcept(*--__end1));
--__end2;
}
}
代码看到这里,基本已经水落石出了,我们看到上边有一个很关键的代码_VSTD::move_if_noexcept(*–__end1),从字面意思也能看出来它是什么意思,接着看下它的源码
emplate
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
typename conditional
::value && is_copy_constructible::value,
const _Tp&,
_Tp&&
>::type
move_if_noexcept(_Tp& __x) _NOEXCEPT
{
return _VSTD::move(__x);
}
这块代码就比较复杂了,move_if_noexcept返回值使用了SFINA的技术,conditional是一个条件判断语句,如果它第一类型是true,则返回const_TP&,如果是false则返回类型_Tp&&,那就看下!is_nothrow_move_constructible::value&&is_copy_constructible::value这个到底表达什么意思,从标准库源代码is_nothrow_move_constructible::value是判断_TP这个类型是否有不抛一场的移动构造方法,is_copy_constructible::value并且拷贝构造方法,
源码看到这里大家心里就很清楚了,到底咋回事!
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
Unity Undo详解 在Unity中,Undo是一个非常重要的功能,它可以让开发者在编辑器中进行操作时,随时撤销之前的操作,从而避免不必要的错误。本文将详细介绍Unity Undo实现原理和使用方法,并提供多个使用例子,帮助开发者更好地理解和应用该功能。 …