If I emplace an element into a std::vector by using emplace or emplace_back, the element will be constructed without needing an operator=.
Now I already have a std::vector with elements, and I want to set an element at an index to a new value. This is my current solution, a new solution should behave the same:
std::vector<Type> vec;
// ... fill the vector
for (int value = 0; value <= 10; ++value)
vec.emplace_back( Type(value) );
// replace at index
int index = 5;
Type newelement {30};
vec.erase( vec.begin() + i );
vec.insert( vec.begin() + i, newelement );
But I obviously don't want to do just that, as that moves all the other elements in the std::vector around, which makes an O(1) complexity task take O(n) time.
Edit:
I changed the code snipped to use insert, which is how it actually is in my current code. I now realize that I am confused by not knowing the difference between insert and emplace. Maybe clarifying that would answer this question, too.
I think you can just use vec[index] = Type(30); which uses move assignment because the new element is temporary. You need move assignment anyway for std::erase.
If for some reason you want to name the temporary element, you could instead use vec[index] = std::move(newelement);.
The code
vec.erase( vec.begin() + i );
vec.insert( vec.begin() + i, newelement );
can be replaced with
std::swap( vec.at( i ), newelement );
This method will not shift the contents of the vector.
Please feel free to edit this answer to show if and how this method differs apart from not shifting the vector contents.
First of all, your example is not correct since you should pass just the arguments of Type's constructor to emplace_back, not a full object. Your loop would look like this
std::vector<Type> vec;
for (int value = 0; value <= 10; ++value)
vec.emplace_back(value);
Then, according to cppreference, when using emplace there is no need to call erase first if an element already exists at the requested position.
The element is constructed through std::allocator_traits::construct,
which typically uses placement-new to construct the element in-place
at a location provided by the container. However, if the required
location has been occupied by an existing element, the inserted
element is constructed at another location at first, and then move
assigned into the required location.
Something like this
// replace at index
int index = 5;
vec.emplace( vec.begin() + index, 30);
constructs an object at the desired position with a better complexity than calling erase first.
Related
I am fairly new to c++, but had a question about vectors. My goal is to remove an element from the vector using erase once I hit my out of bounds condition. This all seems to work fine, except that when I call erase, it will be pointing to the first element, but delete the last. Basically, the loop will continue to iterate and delete every element out of my vector. I am using push_back to add Lasers to my vector elsewhere in the code.
std::vector<Laser> m_Lasers;
for (int i = 0; i != m_Lasers.size(); i++)
{
m_Lasers[i].ClearLaser();
if (m_Lasers[i].GetX() > m_ScreenWidth || m_Lasers[i].GetX() < 0 || m_Lasers[i].GetY() < 0)
{
//erase the vector
m_Lasers.erase(m_Lasers.begin() + i);
i--;
}
}
my =operator is defined as:
void operator=(const Laser& L){};
in my laser class. I think my issue may be with this.
Thank you so much for you help!
What vector::erase does is moving all elements after the erased element forward using assignment, and then destroying the last element. It has to do this to maintain the invariant that the elements in the vector are stored contiguously.
For example, give a vector v of size 4, erasing v[1] essentially does v[1] = v[2]; v[2] = v[3]; v.pop_back(); (These are actually move assignments; std::move is omitted for clarity.)
If your assignment is a no-op (which is not allowed by erase's requirements, by the way), then this will just end up destroying the last element.
I'm trying to make a remove function for an array class that uses an array of pointers to store objects.
So what I've got is a list of pointers to objects declared like so:
objname* list = new objname[100];
Note, its declared as a member of a class, lets call it myClass.
What I want is to make a function to myClass that takes an index as a parameter, and removes the object from list on that index. Here's what I've got and what I want to do.
void myClass::remove(int index)
{
objname* temp = new objname[listlen]; //creating a temporary list to copy values from the "main" list.
//want to copy elements from 0 to index in "this->list" and store inside temp, then skip one element and copy the rest.
}
There might be a better way to get this functionality and if so I'm open to suggestions.
What I want is to make a function to myClass that takes an index as a parameter, and removes the object from list on that index.
You can do this easily with an std::vector and std::next:
#include <vector> // for std::vector
#include <iterator> // for std::next
std::vector<objname> v;
void myClass::remove(int index)
{
v.erase(std::next(v.begin(), index));
}
Obviously you should first check that the vector is large enough for index.
But if what you really want to do is copy a portion of one array into another, again, you can easily do this with standard library components:
std::vector<objname> v = ...;
std::vector<objname> temp(v.begin(), std::next(v.begin(), index));
Here, temp will contain copies of the first index elements of v.
If you use arrays and are not yet allowed to use std::vector then you can do the task by applying two times standard algorithm std::copy
For example, (I suppose that listlen is the size of the original array)
#include <algorithm>
//...
objname* temp = new objname[listlen - 1];
std::copy( std::next( list + index ), list + listlen,
std::copy( list, list + index, temp ) );
Expression std::next( list + index ) can be substituted for list + index + 1 provided that for the both expressions index is less than listlen.
So I have a vector of unsigned ints (vector<unsigned int> is called vector1). I have another vector of a struct I created (vector<struct> is called vector2). vector<int> holds an integer that is the index of the vector<struct>. For example, let's say that vector<int = {5, 17, 18, 19}. That means vector2.at(5) == vector2.at(vector1.at(0)).
In the struct, I have a bool variable called var. In most cases, var is false. I want to delete all of the elements in vector1 that have var = true.
What I did was:
for (unsigned int i = 0; i < vector1.size(); i++)
{
if (vector2.at(vector1.at(i)).var)
vector1.erase(vector.begin() + i);
}
The only problem with this is that it does not delete all of the true elements. I have run the for loop multiple times for all values to be delete. Is this the correct behavior? If it is not, where did I go wrong?
You have to use the erase-remove idiom to delete elements from a vector.
v.erase(std::remove(v.begin(), v.end(), value), v.begin);
std::remove moves the elements to the end of the vector and erase will erase the element from the vector.
You can keep a temporary vector, copy of vector1 and iterate over it in the for loop and delete from vector1.
You are erasing elements in the vector while at the same time iterating over it. So when erasing an element, you always jump over the next element, since you increase i while having just shortened the vector at i (it would be even worse, had you used a proper iterator loop instead of an index loop). The best way to do this would be to seperate both opretions, first "marking" (or rather reordering) the elements for removal and then erasing them from the vector.
This is in practice best done using the erase-remove idiom (vector.erarse(std::remove(...), vector.end())), which first uses std::remove(_if) to reorganize the data with the non-removed elements at the beginning and returns the new end of the range, which can then be used to really delete those removed elements from the range (effectively just shortening the whole vector), using std::vector::erase. Using a C++11 lambda, the removal condition can be expressed quite easily:
vector1.erase(std::remove_if( //erase range starting here
vector1.begin(), vector1.end(), //iterate over whole vector
[&vector2](unsigned int i) //call this for each element
{ return vector2.at(i).var; }), //return true to remove
vector1.end()); //erase up to old end
EDIT: And by the way, as always be sure if you really need std::vector::at instead of just [] and keep in mind the implications of both (in particular the overhead of the former and "maybe insecurity" of the latter).
I'd like to move the unique_ptr's stored in an unsorted vector of them to another vector, that will contain the sorted vector of pointers.
Surely moving a unique_ptr will not automatically erase the element in the first vector? How can I do this?
Example of what I want to do:
std::vector<std::unique_ptr<T> > unsorted, sorted;
// fill the "unsorted" vector
while( unsorted.size() > 0 )
{
const auto it = find_next_element_to_add_to_sorted(unsorted);
sorted.push_back( std::move(*it) );
}
I hope the intent is clear.
UPDATE: my algorithm does not allow sorting in-place. If anyone is feeling nice today (I am not asking, see above for my question), feel free to implement it for this situation and show me. I really need the "sort by move". And I don't really see why the moving would be that much more expensive.
Your code looks basically correct to me, except that it seems like you intend for the moved-from unique_ptr to be erased from the unsorted vector:
std::vector<std::unique_ptr<T> > unsorted, sorted;
// fill the "unsorted" vector
while( unsorted.size() > 0 )
{
const auto it = find_next_element_to_add_to_sorted(unsorted);
sorted.push_back( std::move(*it) );
unsorted.erase(it);
}
After the move it refers to a moved-from unique_ptr and *it == nullptr. It still exists in unsorted and if that is not desired, must be explicitly erased.
In the C++ Primer book, Chapter (3), there is the following for-loop that resets the elements in the vector to zero.
vector<int> ivec; //UPDATE: vector declaration
for (vector<int>::size_type ix = 0; ix ! = ivec.size(); ++ix)
ivec[ix] = 0;
Is the for-loop really assigning 0 values to the elements, or do we have to use the push_back function?
So, is the following valid?
ivec[ix] = ix;
Thanks.
Is the for-loop really assigning 0
values to the elements? Or, we have to
use the push_back finction?
ivec[ix] =0 updates the value of existing element in the vector, while push_back function adds new element to the vector!
So, is the following valid?
ivec[ix] = ix;
It is perfectly valid IF ix < ivec.size().
It would be even better if you use iterator, instead of index. Like this,
int ix = 0;
for(vector<int>::iterator it = ivec.begin() ; it != ivec.end(); ++it)
{
*it = ix++; //or do whatever you want to do with "it" here!
}
Use of iterator with STL is idiomatic. Prefer iterator over index!
Yes, you can use the square brackets to retrieve and overwrite existing elements of a vector. Note, however, that you cannot use the square brackets to insert a new element into a vector, and in fact indexing past the end of a vector leads to undefined behavior, often crashing the program outright.
To grow the vector, you can use the push_back, insert, resize, or assign functions.
Using the array brackets the vector object acts just like any other simple array. push_back() increases its length by one element and sets the new/last one to your passed value.
The purpose of this for loop is to iterate through the elements of the vector.
Starting at element x (when ix is 0) up to the last element (when ix is ivec.size() -1).
On each iteration the current element of the vector is set to 9.
This is what the statement
ivec[ix] = 0;
does. Putting
ivec[ix] = ix;
in the for loop would set all the elements of the vector to their position in the vector. i.e, the first element would have a value of zero (as vectors start indexing from 0), the second element would have a value of 1, and so on and so forth.
Yes, assuming ix is a valid index, most likely: you have a vector of int though and the index is size_type. Of course you may want to purposely store -1 sometimes to show an invalid index so the conversion of unsigned to signed would be appropriate but then I would suggest using a static_cast.
Doing what you are doing (setting each value in the vector to its index) is a way to create indexes of other collections. You then rearrange your vector sorting based on a predicte of the other collection.
Assuming that you never overflow (highly unlikely if your system is 32 bits or more) your conversion should work.