问题
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_ptr
s.
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 const
ness 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