std ::在lambda捕获中移动const std :: vector

时间:2018-09-30 15:51:11

标签: c++ vector lambda move-semantics unique-ptr

动机:

我正在尝试通过lambda捕获将std::vector<std::unique_ptr<some_type>>转移到另一个线程。

由于我需要在函数超出范围时不清理向量,因此需要按值(而不是按引用)获取向量。

由于它是unique_ptrs的向量,因此我需要将其移动(而不是复制)到捕获中。

我正在使用generalized lambda capture在捕获时移动矢量。

用于说明概念的最小程序:

auto create_vector(){
    std::vector<std::unique_ptr<int>> new_vector{};
    new_vector.push_back(std::make_unique<int>(5));
    return std::move(new_vector);
}

int main() {
    const auto vec_const = create_vector();

    [vec=std::move(vec_const)](){
        std::cout << "lambda, vec size: " << vec.size() << std::endl;
    }();
}

问题:

如果我使用的是const本地向量,则由于尝试复制unique_ptr而导致编译失败。 但是,如果删除const限定符,则代码可以编译并运行良好。

auto vec_const = create_vector();

问题:

这是什么原因? const是否会禁用向量的“可移动性”?为什么?

在这种情况下如何确保向量的恒定性?

跟进:

注释和答案提到不能从中移除const类型。听起来很合理,但是编译器错误未能明确说明。在这种情况下,我期望以下两件事之一:

  • std::move(vec_const)应该抛出一个错误,关于不可能从const(将其投射到右值)转移。
  • 向量move-constructor告诉我它拒绝接受const rvalues。

为什么这些不发生?为什么相反,分配似乎只是尝试在向量中复制unique_ptrs(这是我希望从向量copy-constructor获得的结果)?

2 个答案:

答案 0 :(得分:3)

移动是一种破坏性的操作:您从概念上更改了要移动的事物的内容。

所以是的:const对象不能(并且应该)从中移出。那会改变原始对象,从而使其const的性质无效。

在这种情况下,vector没有vector(const vector&&),只有vector(vector &&)(移动构造函数)和vector(const vector &)(副本构造函数)。

重载解析将仅将带有const vector参数的调用绑定到后者(不会违反const正确性),因此将导致复制内容。

我同意:错误报告很糟糕。当您遇到vector的问题时,很难设计关于unique_ptr的错误报告。这就是required from ...., required from ...的整个尾部使视图消失的原因。

从您的问题和代码中,我可以看出您没有完全掌握移动语义学的东西:

  • 您不应该move返回一个返回值;返回值已经是一个右值,所以没有意义。
  • std::move并不会真正移动任何东西,它只会更改要“移出”的变量的限定符,以便可以选择正确的接收者(使用“绑定”规则)。接收功能实际上是更改原始对象的内容。

答案 1 :(得分:3)

当您将某物从A移到B时,那么移动的行为必定意味着A被修改了,因为移动A之后可能不再具有最初是A中的任何内容。这是移动语义的全部目的:提供一个最佳实现,因为可以修改move-from对象:将其内容以某种快速而神秘的方式转移到B中,而将A保留在其中一些有效但未指定的状态。

因此,根据定义,A不能为const