【问题标题】:Move assignment of vector of non-movable-non-copyable objects does not compile不可移动-不可复制对象的向量的移动分配不编译
【发布时间】:2014-12-15 20:03:41
【问题描述】:

以下代码无法使用 Visual Studio 2013 编译:

#include <vector>

struct X {
    X() = default;
    X(const X&) = delete;
    X& operator=(const X&) = delete;
    X(X&&) = delete;
    X& operator=(X&&) = delete;
    ~X() = default;
};

void foo()
{
    std::vector<X> v;
    std::vector<X> w;
    w = std::move(v);
}

错误信息说

error C2280: 'X::X(X &&)' : attempting to reference a deleted function

这对我来说毫无意义。您不需要 X 的移动构造函数来移动 vector&lt;X&gt;。这是编译器错误,还是我遗漏了什么?

这是完整的错误信息:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(600): error C2280: 'X::X(X &&)' : attempting to reference a deleted function
    Test.cpp(9) : see declaration of 'X::X'
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(723) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_Ty &&)' being compiled
    with
    [
        _Ty=X
    ,   _Objty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(723) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_Ty &&)' being compiled
    with
    [
        _Ty=X
    ,   _Objty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(872) : see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,_Ty>(std::allocator<_Ty> &,_Objty *,_Ty &&)' being compiled
    with
    [
        _Alloc=std::allocator<X>
    ,   _Ty=X
    ,   _Objty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(872) : see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,_Ty>(std::allocator<_Ty> &,_Objty *,_Ty &&)' being compiled
    with
    [
        _Alloc=std::allocator<X>
    ,   _Ty=X
    ,   _Objty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory(378) : see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<X,X>(_Ty *,X &&)' being compiled
    with
    [
        _Ty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory(378) : see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<X,X>(_Ty *,X &&)' being compiled
    with
    [
        _Ty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory(416) : see reference to function template instantiation '_FwdIt std::_Uninit_copy<_InIt,_FwdIt,std::allocator<_Ty>>(_InIt,_InIt,_FwdIt,std::_Wrap_alloc<std::allocator<_Ty>> &,std::_Nonscalar_ptr_iterator_tag)' being compiled
    with
    [
        _FwdIt=X *
    ,   _InIt=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ,   _Ty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory(427) : see reference to function template instantiation '_FwdIt std::_Uninit_copy<_Iter,X,_Alloc>(_InIt,_InIt,_FwdIt,_Alloc &)' being compiled
    with
    [
        _FwdIt=X *
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ,   _Alloc=std::_Wrap_alloc<std::allocator<X>>
    ,   _InIt=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(1640) : see reference to function template instantiation '_FwdIt std::_Uninitialized_copy<_Iter,X*,std::_Wrap_alloc<std::allocator<_Ty>>>(_InIt,_InIt,_FwdIt,_Alloc &)' being compiled
    with
    [
        _FwdIt=X *
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ,   _Ty=X
    ,   _InIt=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ,   _Alloc=std::_Wrap_alloc<std::allocator<X>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(789) : see reference to function template instantiation 'X *std::vector<X,std::allocator<_Ty>>::_Ucopy<_Iter>(_Iter,_Iter,X *)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(789) : see reference to function template instantiation 'X *std::vector<X,std::allocator<_Ty>>::_Ucopy<_Iter>(_Iter,_Iter,X *)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(766) : see reference to function template instantiation 'void std::vector<X,std::allocator<_Ty>>::_Construct<_Iter>(_Iter,_Iter,std::forward_iterator_tag)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(766) : see reference to function template instantiation 'void std::vector<X,std::allocator<_Ty>>::_Construct<_Iter>(_Iter,_Iter,std::forward_iterator_tag)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(854) : see reference to function template instantiation 'void std::vector<X,std::allocator<_Ty>>::_Construct<std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>>(_Iter,_Iter)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(854) : see reference to function template instantiation 'void std::vector<X,std::allocator<_Ty>>::_Construct<std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>>(_Iter,_Iter)' being compiled
    with
    [
        _Ty=X
    ,   _Iter=std::move_iterator<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<X>>>>
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(849) : while compiling class template member function 'void std::vector<X,std::allocator<_Ty>>::_Assign_rv(std::vector<_Ty,std::allocator<_Ty>> &&,std::false_type)'
    with
    [
        _Ty=X
    ]
    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector(860) : see reference to function template instantiation 'void std::vector<X,std::allocator<_Ty>>::_Assign_rv(std::vector<_Ty,std::allocator<_Ty>> &&,std::false_type)' being compiled
    with
    [
        _Ty=X
    ]
    Test.cpp(16) : see reference to class template instantiation 'std::vector<X,std::allocator<_Ty>>' being compiled
    with
    [
        _Ty=X
    ]

【问题讨论】:

  • 您也可以更新此内容以包含您对为什么不需要需要对象级移动构造来执行此操作的推测。有些读者看到这个可能不明白为什么你认为没有澄清就不需要它。我的怀疑是@dyp 是正确的;分配器要么没有为propagate_on_container_move_assignment(C++14,顺便说一句)配置,要么忽略了相同的配置。
  • 所写的实际上是在 gcc 4.7.2 上为我编译的。但是,您是否永远无法向您的vector&lt;X&gt; 添加任何内容?
  • 为什么要尝试将不可复制、不可移动的对象放入std::vector&lt;&gt;?这对我来说似乎毫无用处 - 您甚至无法使用 emplace_back() 向其中插入任何内容。如果您使用vector&lt;shared_ptr&lt;X&gt; &gt;vector&lt;X*&gt;,我会理解...

标签: c++ c++11 visual-studio-2013 language-lawyer move-semantics


【解决方案1】:

正如 dyp 在 cmets 中提到的,这是一个 reported bug in C++11*。表达式

a = rv

(其中aX 类型的容器,元素类型为TrvX 类型的非常量右值)
表 99,“分配器感知容器要求”中的以下要求:

如果allocator_traits<allocator_type>::propagate_on_container_move_assignment ::valuefalseTMoveInsertableXMoveAssignable。全部 a 的现有元素要么被移动分配,要么被销毁。

allocator_traits 有以下propagate_on_container_move_assignment 的定义:

typedef 见下文 propagate_on_container_move_assignment;

类型: Alloc::propagate_on_container_move_assignment 如果存在这样的类型,
否则为 false_type

问题是忘记把对应的typedef放入std::allocator,所以propagate_on_container_move_assignment总是false。只需添加 typedef 即可在 C++14 中解决此问题。

* 注意 [default.allocator] 和 [allocator.traits.types] 实际上在 N3337 的 §20.6 中,而不是 §20.7。

【讨论】:

  • propagate_on_container_move_assignment 之所以在这里如此重要与释放有关:如果分配器没有传播,则分配的 lhs 上的分配器必须能够释放分配器分配的内存的 rhs。仅当分配器对象比较相等时才允许这样做 - 运行时决定。如果它们不比较相等,则不能重用内存并且必须移动各个元素。由于这是一个运行时决策,因此必须支持移动分配。
【解决方案2】:

C++11 的答案: VS 符合原始规范,因为根据this 缺陷报告,std::allocator 的规范

导致对具有默认分配器的容器的移动赋值运算符产生不必要的要求(值类型的MoveInsertableMoveAssignable)。

然而,这已在 C++14 中得到修复。所以现在std::allocator 不再使此代码非法,根据 N3797 中的表 96 ([20.2.1,container.requirements.general]),std::vector&lt;T&gt; =: X 的模板参数 T 的要求是

要求:T 可以从 X 中擦除

这是真的,a = rv 对于 X 类型的值 aX 类型的非 cpnst r 值 rv 有要求

a 应等于 rv 的值 在此之前有 任务,

所以对T 没有进一步要求。我没有在 [23.3.6,vector] 中找到对 T 的任何额外要求,因此这应该是 C++14 中的合法代码(如缺陷报告所示)。

【讨论】:

  • 缺陷解决方案被视为追溯适用于它们所针对的文档,因此我们应该说 VS 遵循原始规范,因此是错误的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-06
  • 2013-04-08
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多