为什么 deque::erase() 调用赋值运算符?
Why does deque::erase() invoke assignment operator?
正如标题所说,为什么deque在erase()时调用了包含类型的赋值运算符?我能理解为什么 vector 可能是因为 vector 中的元素在连续的内存中,但是由于 deque 不能保证连续的内存,为什么它会在删除它的一些元素时尝试移动它的元素。
此代码无法编译,因为我的 Contained
类型的赋值运算符被删除并且它没有移动构造函数。
#include <deque>
class Contained
{
public:
Contained() = default;
~Contained() { }
Contained(const Contained&) = delete;
Contained& operator=(const Contained&) = delete;
};
class Container
{
public:
Container()
{
for(int i = 0; i < 5; i++) { m_containerDS.emplace_back(); }
}
~Container() { }
void clear()
{
m_containerDS.erase(m_containerDS.begin(), m_containerDS.end());
}
private:
std::deque<Contained> m_containerDS;
};
int main()
{
return 0;
}
MSVC 编译器发出此错误消息:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(2527): error C2280: 'Contained &Contained::operator =(const Contained &)' : attempting to reference a deleted function
1> ConsoleApplication13.cpp(12) : see declaration of 'Contained::operator ='
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(2548) : see reference to function template instantiation '_BidIt2 std::_Move_backward<_BidIt1,_BidIt2>(_BidIt1,_BidIt1,_BidIt2,std::_Nonscalar_ptr_iterator_tag)' being compiled
1> with
1> [
1> _BidIt2=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> , _BidIt1=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\deque(1622) : see reference to function template instantiation '_BidIt2 std::_Move_backward<std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>,std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>>(_BidIt1,_BidIt1,_BidIt2)' being compiled
1> with
1> [
1> _BidIt2=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> , _BidIt1=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\deque(1601) : while compiling class template member function 'std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>> std::deque<Contained,std::allocator<_Ty>>::erase(std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>,std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>)'
1> with
1> [
1> _Ty=Contained
1> ]
1> ConsoleApplication13.cpp(27) : see reference to function template instantiation 'std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>> std::deque<Contained,std::allocator<_Ty>>::erase(std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>,std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>)' being compiled
1> with
1> [
1> _Ty=Contained
1> ]
1> ConsoleApplication13.cpp(31) : see reference to class template instantiation 'std::deque<Contained,std::allocator<_Ty>>' being compiled
1> with
1> [
1> _Ty=Contained
1> ]
简短回答:因为
Type requirements
-T must meet the requirements of MoveAssignable.
长答案:即使 std::deque
不需要提供连续内存,它仍然需要提供 恒定 复杂性 operator[]
.这就是它必须移动元素的原因。
这是因为 std::deque 通常实现为环形缓冲区,而环形缓冲区又通常实现为单件内存缓冲区。这意味着当您从双端队列中删除元素时,如果删除的元素既不在序列的末尾也不在序列的开头,则可能需要移动一些元素。
这是插图:
V buffer begins here V buffer ends here
1. [ ] [.] [.] [.] [.] [.] [.] [.] [ ] [ ] [ ]
^first element ^last element
2. [ ] [.] [.] [.] [.] [.] [.] [.] [ ] [ ] [ ]
^ you want to remove this element.
<= these elements should be moved
V V V V
3. [ ] [.] [.] [ ] [:] [:] [:] [:] [ ] [ ] [ ]
^ element have been removed.
实际上赋值运算符仅在类型没有移动运算符时才使用。因此,如果您将以下行添加到 class,那么一切都可以正常编译:
Contained& operator=(Contained&&) = default;
更新:
看来我错了,因为大多数 STL 实现现在使用 Dynamic Arrays 的一些变体,而不是环形缓冲区。尽管如此,它们仍然是数组,如果从数组中间移除一个元素,则需要移动元素。
正如标题所说,为什么deque在erase()时调用了包含类型的赋值运算符?我能理解为什么 vector 可能是因为 vector 中的元素在连续的内存中,但是由于 deque 不能保证连续的内存,为什么它会在删除它的一些元素时尝试移动它的元素。
此代码无法编译,因为我的 Contained
类型的赋值运算符被删除并且它没有移动构造函数。
#include <deque>
class Contained
{
public:
Contained() = default;
~Contained() { }
Contained(const Contained&) = delete;
Contained& operator=(const Contained&) = delete;
};
class Container
{
public:
Container()
{
for(int i = 0; i < 5; i++) { m_containerDS.emplace_back(); }
}
~Container() { }
void clear()
{
m_containerDS.erase(m_containerDS.begin(), m_containerDS.end());
}
private:
std::deque<Contained> m_containerDS;
};
int main()
{
return 0;
}
MSVC 编译器发出此错误消息:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(2527): error C2280: 'Contained &Contained::operator =(const Contained &)' : attempting to reference a deleted function
1> ConsoleApplication13.cpp(12) : see declaration of 'Contained::operator ='
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(2548) : see reference to function template instantiation '_BidIt2 std::_Move_backward<_BidIt1,_BidIt2>(_BidIt1,_BidIt1,_BidIt2,std::_Nonscalar_ptr_iterator_tag)' being compiled
1> with
1> [
1> _BidIt2=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> , _BidIt1=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\deque(1622) : see reference to function template instantiation '_BidIt2 std::_Move_backward<std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>,std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>>(_BidIt1,_BidIt1,_BidIt2)' being compiled
1> with
1> [
1> _BidIt2=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> , _BidIt1=std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\deque(1601) : while compiling class template member function 'std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>> std::deque<Contained,std::allocator<_Ty>>::erase(std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>,std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>)'
1> with
1> [
1> _Ty=Contained
1> ]
1> ConsoleApplication13.cpp(27) : see reference to function template instantiation 'std::_Deque_iterator<std::_Deque_val<std::_Deque_simple_types<Contained>>> std::deque<Contained,std::allocator<_Ty>>::erase(std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>,std::_Deque_const_iterator<std::_Deque_val<std::_Deque_simple_types<_Ty>>>)' being compiled
1> with
1> [
1> _Ty=Contained
1> ]
1> ConsoleApplication13.cpp(31) : see reference to class template instantiation 'std::deque<Contained,std::allocator<_Ty>>' being compiled
1> with
1> [
1> _Ty=Contained
1> ]
简短回答:因为
Type requirements
-T must meet the requirements of MoveAssignable.
长答案:即使 std::deque
不需要提供连续内存,它仍然需要提供 恒定 复杂性 operator[]
.这就是它必须移动元素的原因。
这是因为 std::deque 通常实现为环形缓冲区,而环形缓冲区又通常实现为单件内存缓冲区。这意味着当您从双端队列中删除元素时,如果删除的元素既不在序列的末尾也不在序列的开头,则可能需要移动一些元素。 这是插图:
V buffer begins here V buffer ends here
1. [ ] [.] [.] [.] [.] [.] [.] [.] [ ] [ ] [ ]
^first element ^last element
2. [ ] [.] [.] [.] [.] [.] [.] [.] [ ] [ ] [ ]
^ you want to remove this element.
<= these elements should be moved
V V V V
3. [ ] [.] [.] [ ] [:] [:] [:] [:] [ ] [ ] [ ]
^ element have been removed.
实际上赋值运算符仅在类型没有移动运算符时才使用。因此,如果您将以下行添加到 class,那么一切都可以正常编译:
Contained& operator=(Contained&&) = default;
更新: 看来我错了,因为大多数 STL 实现现在使用 Dynamic Arrays 的一些变体,而不是环形缓冲区。尽管如此,它们仍然是数组,如果从数组中间移除一个元素,则需要移动元素。