简体   繁体   English

为什么deque :: erase()会调用赋值运算符?

[英]Why does deque::erase() invoke assignment operator?

Just as the title says, why does deque invoke the assignment operator of the contained type during erase()? 正如标题所说,为什么deque在erase()期间调用包含类型的赋值运算符? I can understand why vector might because elements in a vector are in contiguous memory, but since deque doesn't guarantee contiguous memory, why would it try to move its elements around when some of its elements are removed. 我可以理解为什么向量可能因为向量中的元素在连续的内存中,但由于deque不能保证连续的内存,为什么它会在删除某些元素时尝试移动它的元素。

This code fails to compile because my Contained type's assignment operator is deleted and it doesn't have a move constructor. 此代码无法编译,因为我的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;
}

The MSVC compiler emits this error message: 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>          ]

Short answer: because 简短回答:因为

Type requirements 类型要求

-T must meet the requirements of MoveAssignable. -T必须满足MoveAssignable的要求。

Long answer: even though std::deque doesn't have a requirement to provide contiguous memory, it still has a requirement to provide a constant complextity operator[] . 答案很长:即使std::deque没有提供连续内存的要求,它仍然需要提供一个恒定的完整性operator[] This is why it has to move elements. 这就是它必须移动元素的原因。

This is because std::deque very often implemented as a ring buffer, which in its turn often implemented as single-piece memory buffer. 这是因为std :: deque经常被实现为环形缓冲区,而环形缓冲区又经常被实现为单片内存缓冲区。 That means when you remove elements from the deque it may need to move some elements if erased elements are not at either the end or the beginning of the sequence. 这意味着当您从双端队列中删除元素时,如果已删除的元素不在序列的结尾或开头,则可能需要移动一些元素。 Here is the illustration: 这是插图:

    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.

Actually assignment operator only used if there is no move operator for the type. 实际上,只有在类型没有移动运算符时才使用赋值运算符。 So if you add to your class the following line then everything compiles just fine: 因此,如果您在课程中添加以下行,那么所有内容都可以正常编译:

Contained& operator=(Contained&&) = default;

UPDATE: It seems I was wrong as the most of the STL implementations now use some variants of the Dynamic Arrays , not ring buffers. 更新:似乎我错了,因为大多数STL实现现在使用动态数组的一些变体,而不是环形缓冲区。 Still, they are arrays and there is need to move elements if an element removed from the middle of the array. 但是,它们是数组,如果从数组中间删除了一个元素,则需要移动元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM