在不使用元素复制赋值运算符的情况下复制向量 - 可移植性

时间:2015-11-16 11:37:41

标签: c++ stl portability

如果我有这样的类型:

class Foo {
public:
    Foo();
    Foo(const Foo&);
    Foo& operator=(const Foo&) = delete;

    ...
private:
    ...
};

我有两个这种类型的载体:

std::vector<Foo> x;
std::vector<Foo> y;

我想将x的内容复制到y,是否有跨平台的方式?

VC ++将使用y.assign(x.begin(), x.end())执行此操作,Foo使用y = x的复制构造函数而不是已删除的复制赋值运算符。但是,无论您是尝试y.assign(x.begin(), x.end())还是<?php $this->registerJs(' jQuery(document).on("change" ,"#'. Html::getInputId($model ,'Test2') .'" ,function(){ $("#'. Html::getInputId($model ,'Test3') .'").val(); var first = $("#'. Html::getInputId($model ,'Test1') .'").val(); var second = $("#'. Html::getInputId($model ,'Test2') .'").val(); var third = first +" " + second; $("#'. Html::getInputId($model ,'Test3') .'").val(third); }); '); ?> ,GCC都会抱怨丢失的副本分配运算符。

是否有某种方法适用于两者?

2 个答案:

答案 0 :(得分:4)

该标准要求y.assign(it1, it2)生效,元素类型T CopyAssignable 。也就是说,复制可构造和可分配。您的类型不可分配,因此您无法依赖assigny = x也是如此。该标准的第23.2.3节描述了各种序列容器操作的要求。

如果您有现有的向量y,则可以构建一个新的向量z,然后将其与y交换:

{
  std::vector<Foo> z(x.begin(), x.end());
  z.swap(y);
}

这使用范围构造函数,只需要TEmplaceConstructible,不需要赋值操作符!然后,您可以交换底层内存而无需进一步复制。

请注意,这可能会导致更大的内存使用量,因为y的任何现有内容都会一直存在,直到新交换的z超出范围。您可以先尝试

来尝试缓解这个问题
y.clear();
y.shrink_to_fit();

虽然shrink_to_fit()只是一个请求,但您的库实现可能无法遵守。

[Live demo]

答案 1 :(得分:2)

在C ++ 11之前,您的代码是不可能的:向量元素类型必须是CopyAssignable,这意味着可复制构造和可分配。请注意,如果类型不符合这些要求,则代码格式错误,无需诊断;这使编制者能够接受或拒绝它。

从C ++ 11开始,个别操作都有自己的要求。 vector的相关要求是(来源:C ++ 14表100):

  • emplace_backMoveInsertibleMoveAssignable
  • insertCopyInsertableCopyAssignable
  • 构造函数:EmplaceConstructible

这些特征的含义是(粗略地):

  • CopyInsertable:有copy-constructor
  • MoveInsertable:具有copy-constructor或move-constructor(或两者)
  • CopyAssignable:具有复制构造函数和复制赋值运算符
  • MoveAssignableMoveInsertable,并具有复制分配或移动分配运算符(或两者)
  • EmplaceConstructible:有构造函数

这里的基本原理是任何插入都可能导致内存重新分配,要求您的对象可以被复制或移动。但是,标准不要求编译器测试构造函数或赋值运算符是否都可用,如果不是另一个,则使用一个。相反,它指定两者都必须可用。

您没有指定您的班级是否可以移动。假设它不是,则意味着您不能使用任何emplace或insert方法,也不能使用任何其他可能导致重新分配的函数。

如果您当时正在创建y,那么您当然可以使用初始化:

vector<int> y = x;

如果y已经存在,正如您的问题似乎表明的那样,那么您几乎不运气:您无法使用任何插入功能,因此您只能修改现有元素。由于没有赋值运算符,因此无法通过赋值执行此操作。

但您可以按照Andrew的回答中的建议使用y.swap()

考虑将move-constructor和move-assignment添加到您的类中,或重新设计代码,以便您不需要分配给y。 (例如,使用指针)。

如果x.size() == y.size(),您可以使用展示位置作为最后的黑客攻击:

for (size_t i = 0; i != y.size(); ++i)
{
    y[i].~Foo();
    new(&y[i]) Foo(x[i]);    // copy-construct
}