首页 文章

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

提问于
浏览
3

正如 Headers 所说,为什么deque在erase()期间调用包含类型的赋值运算符?我可以理解为什么向量可能因为向量中的元素在连续的内存中,但由于deque不能保证连续的内存,为什么它会在删除某些元素时尝试移动它的元素 .

此代码无法编译,因为我的 Contained 类型's assignment operator is deleted and it doesn' t有一个移动构造函数 .

#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>          ]

2 回答

  • 4

    简短回答:因为

    类型要求-T必须满足MoveAssignable的要求 .

    答案很长:即使 std::deque 没有要求提供连续内存,它仍然需要提供一个恒定的完整性 operator[] . 这就是它必须移动元素的原因 .

  • 4

    这是因为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.
    

    实际上,只有在类型没有移动运算符时才使用赋值运算符 . 因此,如果您在课程中添加以下行,那么所有内容都可以正常编译:

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

    UPDATE: 似乎我错了,因为大多数STL实现现在使用Dynamic Arrays的某些变体,而不是环形缓冲区 . 但是,它们是数组,如果从数组中间删除了一个元素,则需要移动元素 .

相关问题