Is It Safe to Push_Back an Element from the Same Vector

Is it safe to push_back an element from the same vector?

It looks like http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 addressed this problem (or something very similar to it) as a potential defect in the standard:

1) Parameters taken by const reference can be changed during execution
of the function

Examples:

Given std::vector v:

v.insert(v.begin(), v[2]);

v[2] can be changed by moving elements of vector

The proposed resolution was that this was not a defect:

vector::insert(iter, value) is required to work because the standard
doesn't give permission for it not to work.

Does push_back in vector insert at the same location?

for(int i=0; i<vec.size(); ++i) // --> Deep copy
{
Example newE(*(vec[i]));
newVec.push_back(&newE);
}

In this code you make a copy of vec[i] to Example newE. Then you push_back the address of newE to the newVec vector. Then the newE object goes out of scope and is destroyed, so you end up having a pointer to garbage inside newVec.

If you want a deep copy of vector content, and you want to store owning pointers to objects, consider using vector of smart pointers, e.g. vector<shared_ptr<Example>>.

In this case, you can simply copy the vectors with operator=, and the reference counts of the shared_ptrs will be automatically updated.

Yoy may want to consider also the simpler design of just having vector<Example> (without pointer indirection).

Is std::vector copying the objects with a push_back?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector. If you want to store pointers to objects in your vector, create a std::vector<whatever*> instead of std::vector<whatever>.

However, you need to make sure that the objects referenced by the pointers remain valid while the vector holds a reference to them (smart pointers utilizing the RAII idiom solve the problem).

vector function push_back keeps rewriting the value to the same slot in memory

You're creating a new vector in each call to createTable, not reusing an existing vector; you're not constantly inserting into v[0], you're constantly replacing v with a whole new vector that only has a single element. The second call to createTable should probably just be a direct call to v.push_back(insertedString);.

Alternatively, remove the vcreate declaration and actually use the v passed into the function instead (which is still wasteful, because it's constantly copying and replacing vectors instead of pushing onto an existing one directly, but it would at least be logically correct).

Insert or push_back to end of a std::vector?

After all, they do the same thing. Don't they?

No. They are different. The first method using std::vector::push_back will undergo several reallocations compared to std::vector::insert.

The insert will internally allocate memory, according to the current std::vector::capacity before copying the range. See the following discussion for more:

Does std::vector::insert reserve by definition?


But is there any difference in performance?

Due to the reason explained above, the second method would show slight performance improvement. For instance, see the quick benck-mark below, using http://quick-bench.com:

See online bench-mark

Sample Image

Or write a test program to measure the performance(as @Some programmer dude mentioned in the comments). Following is a sample test program:

#include <iostream>
#include <chrono>
#include <algorithm>
#include <vector>
using namespace std::chrono;

class Timer final
{
private:
time_point<high_resolution_clock> _startTime;

public:
Timer() noexcept
: _startTime{ high_resolution_clock::now() }
{}
~Timer() noexcept { Stop(); }
void Stop() noexcept
{
const auto endTime = high_resolution_clock::now();
const auto start = time_point_cast<microseconds>(_startTime).time_since_epoch();
const auto end = time_point_cast<microseconds>(endTime).time_since_epoch();
const auto durationTaken = end - start;
const auto duration_ms = durationTaken * 0.001;
std::cout << durationTaken.count() << "us (" << duration_ms.count() << "ms)\n";
}
};
// Method 1: push_back
void push_back()
{
std::cout << "push_backing: ";
Timer time{};
for (auto i{ 0ULL }; i < 1000'000; ++i)
{
std::vector<int> vec = { 1 };
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
}
}
// Method 2: insert_range
void insert_range()
{
std::cout << "range-inserting: ";
Timer time{};
for (auto i{ 0ULL }; i < 1000'000; ++i)
{
std::vector<int> vec = { 1 };
int arr[] = { 2,3,4,5 };
vec.insert(std::end(vec), std::cbegin(arr), std::cend(arr));
}
}

int main()
{
push_back();
insert_range();
return 0;
}

release building with my system(MSVS2019:/Ox /std:c++17, AMD Ryzen 7 2700x(8-core, 3.70 Ghz), x64 Windows 10)

// Build - 1
push_backing: 285199us (285.199ms)
range-inserting: 103388us (103.388ms)

// Build - 2
push_backing: 280378us (280.378ms)
range-inserting: 104032us (104.032ms)

// Build - 3
push_backing: 281818us (281.818ms)
range-inserting: 102803us (102.803ms)

Which shows for the given scenario, std::vector::insert ing is about 2.7 times faster than std::vector::push_back.

See what other compilers(clang 8.0 and gcc 9.2) wants to say, according to their implementations: https://godbolt.org/z/DQrq51

Is it safe to push_back 'dynamically allocated object' to vector?

If all you care about is exception-safety of this operation:

v.reserve(v.size()+1);  // reserve can throw, but that doesn't matter
v.push_back(new Foo); // new can throw, that doesn't matter either.

The issue of a vector having responsibility for freeing the objects pointed to by its contents is a separate thing, I'm sure you'll get plenty of advice about that ;-)

Edit: hmm, I was going to quote the standard, but I actually can't find the necessary guarantee. What I'm looking for is that push_back will not throw unless either (a) it has to reallocate (which we know it won't because of the capacity), or (b) a constructor of T throws (which we know it won't since T is a pointer type). Sounds reasonable, but reasonable != guaranteed.

So, unless there's a beneficial answer over on this question:

Is std::vector::push_back permitted to throw for any reason other than failed reallocation or construction?

this code depends on the implementation not doing anything too "imaginative". Failing that, your solution from the question can be templated up:

template <typename T, typename Container>
void push_back_new(Container &c) {
auto_ptr<T> p(new T);
c.push_back(p.get());
p.release();
}

Usage then isn't too tedious:

struct Bar : Foo { };

vector<Foo*> v;
push_back_new<Foo>(v);
push_back_new<Bar>(v);

If it's really a factory function rather than new then you could modify the template accordingly. Passing a lot of different parameter lists in different situations would be difficult, though.

Vector push_back() and direct assignment using [] give different results

vector<string> board(n);

fills the vector already with n elements. If you now add additional! elements with push_back, you have a vector with two times elements. The first half are already in from the constructor of std::vector and the second half pushed in later.

If you use the default constructor

vector<string> board;

in combination with push_back, you will get the same results as with your example code.

For large vectors the solution with upfront initialization of n elements and access via operator[] can be much faster, because there is no need of reallocation and copy operations inside the vector. If speed is important: measure!

Vector.push_back() adds same Element while reading from File

As mentioned in the comments, you are pushing the pointers back not the actual strings. To get the actual strings you can do this:

void readFileToVec()
{
ifstream file;
file.open ("rb");
vector<string> v;
string word;
while (file >> word)
{
v.push_back(word);
}
}

This will work if the elements are all strings and are seperated by a space,tab, or newline. If your words are seperated by anything other than one of these three (for example a list of words separated by commas) then you can use getline and specify the separator.

In any case, reading about C++ streams and the difference between C-style strings and STL strings would be worthwhile if you intend to do this sort of thing with C++ again. You are using C Strings and old school FILE which is part of the C Library while C++ provides you with utilities to make your life easier. File streams and C++ strings are great examples of such utilities.

push_back() is not adding element to the vector? (C++ Tree Traversal)

You're ignoring the results from each recursion. You should be doing this:

vector<int> inorderTraversal(TreeNode *root)
{
vector<int> order;
if (root != nullptr)
{
order = inorderTraversal(root->left);
cout << "pushing back : " << root->val << std::endl;
order.push_back(root->val);
auto tmp = inorderTraversal(root->right);
order.insert(order.end(), tmp.begin(), tmp.end());
}

return order;
}

Much More Efficient

That said, if you counted the number of local vectors created in this, though short, they will be many (as many as there are nodes in your tree, in fact). You can eliminate all of those middle-men vectors by providing a shim between the actual traversal and the creation of the order vector:

void inorderTraversal_v(TreeNode const *root, std::vector<int>& order)
{
if (root != nullptr)
{
inorderTraversal(root->left, order);
order.push_back(root->val);
inorderTraversal(root->right, order);
}

return order;
}

std::vector<int> inorderTraversal(TreeNode const *root)
{
std::vector<int> order;
inorderTraversal_v(root, order);
return order;
}

Doing this creates a single vector, and in so doing eliminates (N-1) temporary vectors along the way, where N is the number of nodes in the tree.



Related Topics



Leave a reply



Submit