Resizing a std::vector using move insertion and copy insertion - c++

In the C++ standard vector.capacity section, it defines two overloads for resize(). (see also https://en.cppreference.com/w/cpp/container/vector/resize)
This overload requires that the type T is MoveInsertable and DefaultInsertable:
constexpr void resize(size_type sz);
The other overload requires that the type T is CopyInsertable:
constexpr void resize(size_type sz, const T& c);
My question is that why doesn't the second overload try to move from the existing values from the old vector, and further copy-insert new values from the supplied c argument. Wouldn't that be more efficient?

I agree that the second parameter could be an rvalue reference - this saves one copy, when it is subsequently copied from the first newly appended item. I assume what you have in mind is something like this:
std::vector<LargeObject> v;
// ...
LargeObject obj = setupLotsOfResources();
// Now do 1 move and 9 copies instead of 10 copies
v.resize(10, std::move(obj));
However, I would consider this an edge case, and working with rvalue references that are used for 1 move construction and N-1 copies is quite a confusing API. As you are free to use the std::vector API with what is already there such that for the above example, you will have 1 move and N-1 copies, I believe the rationale behind the existing function signature is ease of use and a straightforward signature that doesn't require much studying of the specs to understand what it does.

Related

Move semantics and overload

I think my understanding of rvalue references and move semantics has some holes in it.
As far as I've rvalue references understood now, I could implement a function f in two ways such that it profits from move semantics.
The first version: implement both
void f(const T& t);
void f(T&& t);
This would result in quite some redundancy, as both versions are likely to have (almost) identical implementation.
second version: implement only
void f(T t);
Calling f would result in calling either the copy or the move constructor of T.
Question.
How do the two versions compare to each other? My suspicion:
In the second version, (ownership of) dynamically allocated data may be moved by the move constructor, while non-dynamically allocated data in t and its members needs to be copied. In the first version, neither version allocates any additional memory (except the pointer behind the reference).
If this is correct, can I avoid writing the implementation of f basically twice without the drawback of the second version?
If you need to take a T&& parameter, it usually means you want to move the object somewhere and keep it. This kind of function is typically paired up with a const T& overload so it can accept both rvalues and lvalues.
In this situation, the second version (only one overload with T as a parameter) is always less efficient, but most likely not by much.
With the first version, if you pass an lvalue, the function takes a reference and then makes a copy to store it somewhere. That's one copy construction or assignment. If you pass an rvalue, the function again takes a reference and then moves the object to store it. That's one move construction or assignment.
With the second version, if you pass an lvalue, it gets copied into the parameter, and then the function can move that. If you pass an rvalue, if gets moved (assuming the type has a move constructor) into the parameter, and then the function can also move that. In both cases, that's one more move construction or assignment than with the first version.
Also note that copy elision can happen in some cases.
The benefit of the second version is that you don't need multiple overloads. With the first version, you need 2^n overloads for n parameters that you need copied or moved.
In the simple case of just one parameter, I sometimes do something like this to avoid repeating code:
void f(const T& t) {
f_impl(t);
}
void f(T&& t) {
f_impl(std::move(t));
}
// this function is private or just inaccessible to the user
template<typename U>
void f_impl(U&& t) {
// use std::forward<U>(t)
}

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

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)?
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.
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.

Move reference overload for element access in containers? [duplicate]

The standard C++ containers offer only one version of operator[] for containers like vector<T> and deque<T>. It returns a T& (other than for vector<bool>, which I'm going to ignore), which is an lvalue. That means that in code like this,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject will be copy constructed. Given that makeVector() returns an rvalue vector, it seems reasonable to expect copyOfObject to be move constructed.
If operator[] for such containers was overloaded for rvalue and lvalue objects, then operator[] for rvalue containers could return an rvalue reference, i.e., an rvalue:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
In that case, copyOfObject would be move constructed.
Is there a reason this kind of overloading would be a bad idea in general? Is there a reason why it's not done for the standard containers in C++14?
Converting comment into answer:
There's nothing inherently wrong with this approach; class member access follows a similar rule (E1.E2 is an xvalue if E1 is an rvalue and E2 names a non-static data member and is not a reference, see [expr.ref]/4.2), and elements inside a container are logically similar to non-static data members.
A significant problem with doing it for std::vector or other standard containers is that it will likely break some legacy code. Consider:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
That last line will stop compiling if operator[] on an rvalue vector returned an xvalue. Alternatively - and arguably worse - if there is a foo(const int &) overload, it will silently start calling that function instead.
Also, returning a bunch of elements in a container and only using one element is already rather inefficient. It's arguable that code that does this probably doesn't care much about speed anyway, and so the small performance improvement is not worth introducing a potentially breaking change.
I think you will leave the container in an invalid state if you move out one of the elements, I would argue the need to allow that state at all. Second, if you ever need that, can't you just call the new object's move constructor like this:
T copyObj = std::move(makeVector()[0]);
Update:
Most important point is, again in my opinion, that containers are containers by their nature, so they should not anyhow modify the elements inside them. They just provide a storage, iteration mechanism, etc.

How exactly do I use the functions push_back and pop_back()? I looked them up in the following liks but still don't understand

http://www.cplusplus.com/reference/vector/vector/push_back/ (C++11 Version)
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?;
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
http://www.cplusplus.com/reference/vector/vector/pop_back/
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
If you are a beginner, just read over the additional qualifiers like const, & and &&. The methods in the STL are implemented in a way, that they behave consistent over all overloads:
I will give you a small example here:
std::vector<int> myvector;
myvector.push_back(5);
int five = 5;
myvector.push_back(five);
Now the more in depth part of the answer:
First (const value_type& val). The & character signals, that we take the argument by reference, that means we don't copy the argument, but get a fancy pointer, that will behave like the object itself.
You may not want, that your variable is changed, if you push it back to a vector. To get a promise, by the programmer of the STL, that he will not change your variable while pushing it back to the vector, he can add the const before the type.
The reason it is implemented that way, is that it may prevent an unneeded copy. (First copy the argument onto the stack to call push_back and the second time copy it at the position in the vector. The first copy is unnecessary and saved by the const reference.)
This is all nice and simple, but there are cases, where the compiler is not allowed to take a reference of a value and pass it to a function. In case of temporary values, there is no reference to take, because there is no variable in memory. Take the following line for example.
myvector.push_back(5);
Since the 5 has no address, it can't be passed as a reference. The compiler can not use the first overload of the function. But the programmer also does not want to waste the time for the copy onto the stack. That is why C++11 added new semantic. A so called rvalue for such temporary objects. If you want to write a function to take such an rvalue, you can do so by using type&& rvalue_variable. The value in this case the 5 is moved onto the stack by using the move constructor of the type. For trivial types like int, this will be the same as the copy constructor. For complex types like std::vector there are shortcuts one can take if one is allowed to rip the temporary object apart. In case of the vector, it does not need to copy all the data in the vector to a new location, but can use the pointer of the old vector in the new object.
Now we can look at the example again:
std::vector<int> myvector;
myvector.push_back(5); // push_back(const int&) can't be applied. The compiler chooses push_back(int&&) for us
int five = 5;
myvector.push_back(five); // push_back(const int&) can be applied and is used by the compiler
// The resulting vector after this has the two values [5, 5]
// and we see, that we don't need to care about it.
This should show you how you can use both of them.
push_back():
std::vector<int> vec = { 0, 1, 2 };
vec.push_back(3);
pop_back():
vec.pop_back();
vec.pop_back();
If you need more clarification:
push_back(const T& val) adds its parameter to the end of the vector, effectively increasing the size by 1 iff the vector capacity will be exceeded by its size.
pop_back() doesn't take any parameters and removes the last element of the vector, effectively reducing the size by 1.
Update:
I'm trying to tackle your questions one by one, if there is anything unclear, let me know.
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?;
Prior to C++11, rvalue-references didn't exist. That's why push_back was implemented as vector.push_back(const value_type& val). If you have a compiler that supports C++11 or later, std::vector.push_back() will be overloaded for const lvalue references and rvalue references.
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
You as a programmer do NOT choose how you pass arguments to push_back(), the compiler does it for you automagically, in most cases.
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
value_type is equal to the type of vector that you declared. If a vector is declared with std::string, then it can only hold std::string.
std::vector<std::string> vec;
vec.push_back("str"); // Ok. "str" is allowed.
vec.push_back(12); // Compile-time error. 12 is not allowed.
What is the difference and/or advantages of void push_back (const value_type& val); & void push_back (value_type&& val) and which do you suggest I use?
void push_back(const value_type&) takes an argument, that is then copied into the vector. This means that a new element is initialized as a copy of the passed argument, as defined by an appropriate allocator.
void push_back(value_type&&) takes an argument, that is then moved into the container (this type of expressions are called rvalue expressions).
The usage of either of two depends on the results you want to achieve.
I don't understand how to fill in the arguments (const value_type& val) & (value_type&& val)
In most cases you shouldn't think about which version to use, as compiler will take care of this for you. Second version will be called for any rvalue argument and the first one for the rest. In a rare case when you want to ensure the second overload is called you can use std::move to explicitly convert the argument expression into xvalue (which is a kind of rvalues).
I don't understand the second sentence under the parameter section. (It's a bit too wordy for me to get). I do understand what val is though
The sentence in question is:
Member type value_type is the type of the elements in the container, defined in vector as an alias of its first template parameter (T).
This means that value_type is the same type as the type of vector's elements. E.g., if you have vector<int> then value_type is the same as int and for vector<string> the value_type is string.
Because vector is not an ordinary type, but a template, you must specify a type parameters (which goes into angle brackets <> after vector) when defining a variable. Inside the vector template specification this type parameter T is then aliased with value_type:
typedef T value_type;
It doesn't give an example I can understand real well. Can I get other examples using vectors or some video links that explain the use of the function in practice better?
The main thing you need to remember is that vector behaves like a simple array, but with dynamicly changeable size and some additional information, like its length. push_back is simply a function that adds a new element at the end of this pseudo-array. There is, of course, a lot of subtle details, but they are inconsequential in most of the cases.
The basic usage is like this:
vector<int> v; // v is empty
v.push_back(1); // v now contains one element
vector<float> v2 { 1.0, 2.0 }; // v2 is now a vector with two elements
float f = v2.pop_back(); // v2 now has one element, and f is now equals 2.0
The best way to understand how it works is to try using it yourself.

Why isn't operator[] overloaded for lvalues and rvalues?

The standard C++ containers offer only one version of operator[] for containers like vector<T> and deque<T>. It returns a T& (other than for vector<bool>, which I'm going to ignore), which is an lvalue. That means that in code like this,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject will be copy constructed. Given that makeVector() returns an rvalue vector, it seems reasonable to expect copyOfObject to be move constructed.
If operator[] for such containers was overloaded for rvalue and lvalue objects, then operator[] for rvalue containers could return an rvalue reference, i.e., an rvalue:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
In that case, copyOfObject would be move constructed.
Is there a reason this kind of overloading would be a bad idea in general? Is there a reason why it's not done for the standard containers in C++14?
Converting comment into answer:
There's nothing inherently wrong with this approach; class member access follows a similar rule (E1.E2 is an xvalue if E1 is an rvalue and E2 names a non-static data member and is not a reference, see [expr.ref]/4.2), and elements inside a container are logically similar to non-static data members.
A significant problem with doing it for std::vector or other standard containers is that it will likely break some legacy code. Consider:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
That last line will stop compiling if operator[] on an rvalue vector returned an xvalue. Alternatively - and arguably worse - if there is a foo(const int &) overload, it will silently start calling that function instead.
Also, returning a bunch of elements in a container and only using one element is already rather inefficient. It's arguable that code that does this probably doesn't care much about speed anyway, and so the small performance improvement is not worth introducing a potentially breaking change.
I think you will leave the container in an invalid state if you move out one of the elements, I would argue the need to allow that state at all. Second, if you ever need that, can't you just call the new object's move constructor like this:
T copyObj = std::move(makeVector()[0]);
Update:
Most important point is, again in my opinion, that containers are containers by their nature, so they should not anyhow modify the elements inside them. They just provide a storage, iteration mechanism, etc.