Is std :: vector <T> :: resize (n, val) sufficient for initialization?

This is a C ++ 11 related question. Suppose I already have a vector std::vector<T> v , and I want to change it to n elements initialized with the existing value of T val . (A typical usecase: vector is a member of the instance that is being processed).

What are the pros and cons of the following methods and the most effective?

1) Is std::vector<T>::resize( n, val ) ) enough for initialization?

 v.clear(); v.resize( n, val ); 

2) If not, then I assume that the following is true:

 v.clear(); v.resize(n); std::fill( v.begin(), v.end(), val ); 

3) How about sharing?

 v.swap( std::vector<T>( n, val ) ); 
+5
source share
4 answers

(4)

 std::fill(v.begin(), std::min(v.begin() + n, v.end()), val); v.resize(n, val); 

If T has a suitable assignment behavior that is at least cheaper than building a new one, then use (4). This is the case for T = int (assignment and construction are the same), and T = std :: string (assignment can be faster than creation, because it can use an existing buffer).

If T has the same cost of assignment and construction (for example, T = int), then (1) can also be used for clarity without loss of performance.

If T cannot be assigned or for some reason the assignment is more expensive than building (rarely), use (1)

(1) could be simplified using v.assign(n, val); (kudos to @Casey)

I don't know if (4) assign will be the same in performance. I don’t know whether to assign (ironically, given the name) assign new elements to existing ones or create them again.

Edit: there may be an improvement (4) that I have not tested. It can avoid the overhead of copying / moving while changing the vector capacity.

 if (n <= v.capacity()) { std::fill(v.begin(), std::min(v.begin() + n, v.end()), val); v.resize(n, val); } else { v.assign(n, val); } 
+5
source

Why not use an interface designed specifically for this job?

 v.assign(n, val); 

Documentation here

+8
source

(1). (3) also works. The difference is that (1) does not free memory if the new size is smaller than the current one. (3) always allocates a new piece of memory and deletes the old one.

(2) slower than (1), in general, because it first creates items by default and then assigns them. It may not even compile if T not constructive by default.

+3
source

Lets break it down to show the differences between each of them. I will use n as the new size, m as the old size.

1.

 v.clear();//keeps the same buffer, but calls the destructor on all the values v.resize(n, val);//only makes a new buffer if the value is bigger, does no moves. 

2.

 v.clear();//keeps the same buffer, but calls the destructor on all the values v.resize(n);//initializes all the values to default std::fill( v.begin(), v.end(), val );//initializes all the values again to the new value 

3.

 v.swap( std::vector<T>( n, val ) );//calls destructor on all values in v, cannot reuse the buffer, initializes all the values to the new value 

The differences are subtle but real. 1 can (but not protected) reuse a buffer, which can save overhead. 2 matches 1, but performs double reinitialization. 3 is the same as 1, but it cannot reuse the buffer.

Personally, I believe that the differences between the three are in most cases too subtle with respect to matter, and 1 is the most readable.

+3
source

Source: https://habr.com/ru/post/1210994/


All Articles