std::move a const std::vector in a lambda capture

独自空忆成欢 提交于 2020-04-30 07:44:07

问题


Motivation:

I'm trying to transfer a std::vector<std::unique_ptr<some_type>> to a different thread, via a lambda capture.

Since I need the vector to not be cleaned up when the function goes out of scope, I need to take it by value (and not by reference).

Since it's a vector of unique_ptrs, I need to move (and not copy) it into the capture.

I'm using a generalized lambda capture to move the vector while capturing.

Minimal program to illustrate the concept:

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;
    }();
}

Issue:

If I'm using a const local vector, compilation fails due to attempting to copy the unique_ptrs. However if I remove the const qualifier, the code compiles and runs well.

auto vec_const = create_vector();

Questions:

What's the reason for this? Does being const disable the "movability" of the vector? Why?

How would I ensure the constness of a vector in such a scenario?

Follow-up:

The comments and answers mention that a const type can't be moved from. Sounds reasonable, however the the compiler errors fail to make it clear. In this case I would expect one of two things:

  • The std::move(vec_const) should throw an error regarding moving from const (casting it to rvalue) being impossible.
  • The vector move-constructor telling me that it refuses to accept const rvalues.

Why don't those happen? Why does instead the assignment seems to just try to copy the unique_ptrs inside the vector (which is what I'd expect from the vectors copy-constructor)?


回答1:


When you are moving something from A to B, then act of moving must necessarily mean that A gets modified, since after the move A may no longer have whatever was in A, originally. This is the whole purpose of move semantics: to provide an optimal implementation since the moved-from object is allowed to be modified: its contents getting transferred in some fast and mysterious way into B, leaving A in some valid, but unspecified, state.

Consequently, by definition, A cannot be const.




回答2:


Moving is a disruptive operation: you conceptually change the content of the thing you move from.

So yes: a const object can (and should) not be moved from. That would change the original object, which makes its constness void.

In this case, vector has no vector(const vector&&), only vector(vector &&) (move constructor) and vector(const vector &) (copy constructor).

Overload resolution will only bind a call with const vector argument to the latter (lest const-correctness would be violated), so this will result in copying the contents.

I agree: error reporting sucks. It's hard to engineer an error report about vector when you hit a problem with unique_ptr. That's why the whole tail of required from ...., required from ... obliterates the view.

From your question, and your code, I can tell that you don't fully grasp the move semantics stuff:

  • you shouldn't move into a return value; a return value is already an rvalue, so there's no point.
  • std::move does not really move anything, it only changes the qualifier of the variable you want to 'move from', so that the right receiver can be selected (using 'binding' rules). It is the receiving function that actually changes the contents of the original object.


来源:https://stackoverflow.com/questions/52579524/stdmove-a-const-stdvector-in-a-lambda-capture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!