为什么不能分配给自己缺少复制操作符的对象向量?

时间:2016-05-18 20:04:36

标签: c++ c++11 vector stdvector

我有一个可复制构造的结构向量,但不可赋值:

std::vector<Struct>

这一切都很好。事实上,我甚至可以将值std::vector<TextFragment> v1, v2; v2 = v1; 传递给函数的返回值。然而,这失败了:

private void insert_Click(object sender, EventArgs e)
{
    SqlConnection Conectare = new SqlConnection();
    Conectare.ConnectionString = @"Data Source=.\SQLEXPRESS; 
        AttachDbFilename=|DataDirectory|\Cuvinte.mdf;
        Integrated Security=True;User    
       Instance=True ";
    SqlDataAdapter da = new SqlDataAdapter();
    da.InsertCommand = new SqlCommand("INSERT INTO Tabel_Cuvinte(id,cuv_ger,cuv_eng)"+" VALUES (@id, @cuv_ger,@cuv_eng)", Conectare);

    da.InsertCommand.Parameters.Add("@id", SqlDbType.Int).Value = txtnr.Text;
    da.InsertCommand.Parameters.Add("@cuv_ger", SqlDbType.VarChar).Value = txtger.Text;
    da.InsertCommand.Parameters.Add("@cuv_eng", SqlDbType.VarChar).Value= txteng.Text;
    Conectare.Open();
    da.InsertCommand.ExecuteNonQuery();
    Conectare.Close();
}

错误当然是:

  

错误:C2280:'Struct&amp; Struct :: operator =(const Struct&amp;)':   试图引用已删除的功能

我不明白它为什么试图调用它。这是否是某种优化以避免重新分配向量的内存块?..

3 个答案:

答案 0 :(得分:4)

  

这是否是某种优化以避免重新分配向量的内存块?..

几乎。这是一种优化,可以避免重新分配vector value_type中可能存在的任何内存块。也就是说,这是一个全局假设,即赋值可以比复制构造后的破坏更有效。

例如,考虑vector<string>分配,对于两个大小相等的vector,以及string中每个小点中的一堆大小相同的vector

v2 = v1;

所有这一操作都必须memcpy每个string。根本没有分配。减少分配/解除分配是目前存在的最重要的优化之一。

但是,Struct并没有丢失所有内容。您要做的是指示vector您不希望分配Struct,而是破坏它们,然后从v1复制它们。这样做的语法是:

v2.clear();
for (const auto& x : v1)
    v2.push_back(x);

如下面的评论中所述,您还可以复制构建v1,然后复制swap。您需要创建v1的临时本地副本,或者需要在&#34; lhs&#34;上使用v1。成员互换:

std::vector<Struct>(v1).swap(v2);

我个人认为这很难读。在C ++ 11/14中,我更喜欢这种涉及移动分配的替代方案:

v2 = std::vector<Struct>(v1);

这些替代方案在眼睛上更容易。但使用clear()push_back的第一种方法是平均效率最高的方案。这是因为第一个替代方案是唯一有可能在capacity()中重用v2的方案。其他两个总是capacity()的副本中重新创建新的v1,并丢弃v2现有的capacity()

答案 1 :(得分:3)

std::vector是一个支持分配器的容器。如果我们查看规范(表99),我们a = t其中a是非常数左值,而t是左值或常数右值,需要

  

T是CopyInsertable到X和 CopyAssignable 。发布:a == t

强调我的

其中Tvalue_type({1}}(容器)的X。它还指出该操作是线性的。由于T必须为CopyAssignableStruct不是CopyAssignable,因此无需工作。

这意味着赋值操作看起来像:

std::vector<T>& operator=(const std::vector<T>& rhs)
{
    // allocate enough room for data if needed
    for (std::size_t i = 0; i < rhs.size(); ++i)
        data[i] = rhs.data[i];
    return *this;
}

答案 2 :(得分:0)

您可以从libc ++中找到此功能的信息。请特别注意对std::copy的调用。

template <class _Tp, class _Allocator>
template <class _ForwardIterator>
typename enable_if
<
    __is_forward_iterator<_ForwardIterator>::value &&
    is_constructible<
       _Tp,
       typename iterator_traits<_ForwardIterator>::reference>::value,
    void
>::type
vector<_Tp, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last)
{
    size_type __new_size = static_cast<size_type>(_VSTD::distance(__first, __last));
    if (__new_size <= capacity())
    {
        _ForwardIterator __mid = __last;
        bool __growing = false;
        if (__new_size > size())
        {
            __growing = true;
            __mid =  __first;
            _VSTD::advance(__mid, size());
        }
        pointer __m = _VSTD::copy(__first, __mid, this->__begin_);
        if (__growing)
            __construct_at_end(__mid, __last, __new_size - size());
        else
            this->__destruct_at_end(__m);
    }
    else
    {
        deallocate();
        allocate(__recommend(__new_size));
        __construct_at_end(__first, __last, __new_size);
    }
}

operator =在修复分配器后,只需调用assign(r.begin(), r.end())即可。