I have a piece of code which creates a std::vector<T> with a known size:
std::vector<T> vectorOfTs(n);
Does calling push_back increase the size to n+1?
vectorOfTs.push_back(T());
Yes; note that vector<T>.capacity() is different from vector<T>.size(). The latter denotes the number of elements currently in the vector while the former represents the number of items that fit in the space currently allocated for the vector's internal buffer.
Almost. If there are no exceptions, then size() will increment.
push_back(T()) could also throw an exception at various stages: see here, or summarily:
T() construction, in which case no call to push_back takes place, and size() is unaffected
if the vector needs to increase the capacity, that may throw, in which case size() is unaffected
the vector element will be copy or move constructed using std::allocator_traits<A>::construct(m, p, v);, if A is std::allocator<T>, then this will call placement-new, as by ::new((void*)p) T(v): if any of this throws the vector's size() is unaffected, ****unless***
the move constructor isn't noexcept and does throw: in which case the effects are unspecified
the vector update's then complete - size() will have incremented and the value will be in the vector (even if T::~T())
Yes. If you instead want to reserve space, call reserve(), e.g.:
std::vector<T> vectorOfTs;
vectorOfTs.reserve(n);
// now size() == 0, capacity() >= n
vectorOfTs.push_back(T());
// now size() == 1
Yes.
std::vector<T> vectorOfTs(n);
In the above statement, actually you are constructing 'n' number of new instances of type T (i.e. default constructor T() would be triggered for each time). Now vector vectorOfTs contains n elements. The following version of the vector constructor would be invoked for the above statement.
explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() );
So, when you push back another element into vector, size of vector would be n+1.
Related
Consider a std::vector:
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
Would vec.clear() and vec = std::vector<int>() do the same job? What about the deallocation in second case?
vec.clear() clears all elements from the vector, leaving you with a guarantee of vec.size() == 0.
vec = std::vector<int>() calls the copy/move(Since C++11) assignment operator , this replaces the contents of vec with that of other. other in this case is a newly constructed empty vector<int> which means that it's the same effect as vec.clear();. The only difference is that clear() doesn't affect the vector's capacity while re-assigning does, it resets it.
The old elements are deallocated properly just as they would with clear().
Note that vec.clear() is always as fast and without the optimizer doing it's work most likely faster than constructing a new vector and assigning it to vec.
They are different:
clear is guaranteed to not change capacity.
Move assignment is not guaranteed to change capacity to zero, but it may and will in a typical implementation.
The clear guarantee is by this rule:
No reallocation shall take place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity()
Post conditions of clear:
Erases all elements in the
container. Post: a.empty()
returns true
Post condition of assignment:
a = rv;
a shall be equal to
the value that rv
had before this
assignment
a = il;
Assigns the range
[il.begin(),il.end()) into a. All existing
elements of a are either assigned to or
destroyed.
Consider the following code.
struct MyData{
MyData(const BYTE* pData, size_t uSize)
: bucket_(pData, pData + uSize)
{}
std::vector<BYTE> bucket_;
};
Does my bucket_ do the reserve first when initializing from a pair of iterators? Something like vec.reserve(std::distance(begIter, endIter)).
Or it just simply perform a serious of push_back or back_inserter_iterator::operator= ?
If it does not, I may need to initialize it with uSize of 0 then do the memcpy_s in constructor block.
Does my bucket_ do the reserve first when initializing from a pair of iterators?
Yes, it does in effect.
Standard draft:
Complexity: Makes only N calls to the copy constructor of T (where N is the distance between first and last) and no reallocations if iterators first and last are of forward, bidirectional, or random access categories. It makes order N calls to the copy constructor of T and order log(N) reallocations if they are just input iterators.
(Pointers are random access iterators)
Yes, it's guaranteed that there will be no reallocations, because pointers are RandomAccessIterators. vector.cons/9
template <class InputIterator>
vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
Effects: Constructs a vector equal to the range [first, last), using the specified allocator.
Complexity: Makes only N calls to the copy constructor of T (where N is the distance between first and last) and no
reallocations if iterators first and last are of forward,
bidirectional, or random access categories. It makes order N calls
to the copy constructor of T and order log(N) reallocations if
they are just input iterators.
Which operation is most costly in C++?
1. Resize of a vector (decrease size by 1)
2. Remove last element in vector
From http://en.cppreference.com/w/cpp/container/vector which essentially quotes the Standard:
void pop_back();
Removes the last element of the container.
No iterators or references except for back() and end() are invalidated.
void resize( size_type count );
Resizes the container to contain count elements. If the current size
is greater than count, the container is reduced to its first count
elements as if by repeatedly calling pop_back().
So in this case, calling resize(size() - 1) should be equivalent to calling pop_back(). However, calling pop_back() is the right thing to do as it expresses your intent.
NOTE: the answer is reflecting the changed interface of C++11's std::vector::resize(), which used to contain a hidden default argument which was being copied around (and which may or may not have been optimized away).
In my opinion they are equivalent. The both operations remove the last element and decrease the size.:)
According to the C++ Standard
void resize(size_type sz); 12 Effects: If sz <= size(), equivalent to
calling pop_back() size() - sz times
So they are simply equivalent according to my opinion and the point of view of the Standard.:)
Also if to consider member function erase instead of pop_back (in fact they do the same in this case) then according to the same Standard
4 Complexity: The destructor of T is called the number of times equal
to the number of the elements erased, but the move assignment
operator of T is called the number of times equal to the number of
elements in the vector after the erased elements.
As there are no move operations for the last element then the cost is the same.
I'm trying to use another project's code and they have structs of this form:
struct data{
std::vector<sparse_array> cols,rows;
}
struct sparse_array {
std::vector<unsigned int> idxs;
std::vector<double> values;
void add(unsigned int idx, double value) {
idxs.push_back(idx);
values.push_back(value);
}
}
For my code, I tried using the following lines:
data prob;
prob.cols.reserve(num_cols);
prob.rows.reserve(num_rows);
// Some loop that calls
prob.cols[i].add(idx, value);
prob.rows[i].add(idx, value);
And when I output the values, prob.rows[i].value[j] to a file I get all zeros. But when I use resize instead of reserve I get the actual value that I read in. Can someone give me an explanation about this?
Function reserve() simply allocates a contiguous region of memory big enough to hold the number of items you specify and moves the vector's old content into this new block, which makes sure no more reallocations for the vectors' storage will be done upon insertions as long as the specified capacity is not exceeded. This function is used to reduce the number of reallocations (which also invalidate iterators), but does not insert any new items at the end of your vector.
From the C++11 Standard, Paragraph 23.3.6.3/1 about reserve():
A directive that informs a vector of a planned change in size, so that it can manage the storage
allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve(). If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.
Notice that by doing prob.cols[i].push_back(idx, value); you are likely to get undefined behavior, since i is probably an out-of-bounds index.
On the other hand, function resize() does insert items at the end of your vector, so that the final size of the vector will be the one you specified (this means it can even erase elements, if you specify a size smaller than the current one). If you specify no second argument to a call to resize(), the newly inserted items will be value-initialized. Otherwise, they will be copy-initialized from the value you provide.
From the C++11 Standard, Paragraph 23.3.6.3/9 about resize():
If sz <= size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends
sz - size() value-initialized elements to the sequence.
So to sum it up, the reason why accessing your vector after invoking resize() gives the expected result is that items are actually being added to the vector. On the other hand, since the call to reserve() does not add any item, subsequent accesses to non-existing elements will give you undefined behavior.
If the vector is empty, then std::vector::resize(n) expands the content of this vector by inserting n new elements at the end. std::vector::reserve(n) only reallocates the memory block that your vector uses for storing its elements so that it's big enough to hold n elements.
Then when you call prob.cols[i], you are trying to access the element at index i. In case you used reserve before, this results in accessing the memory where no element resides yet, which produces the undefined behavior.
So just use resize in this case :)
std::vector<int> v1(1000);
std::vector<std::vector<int>> v2(1000);
std::vector<std::vector<int>::const_iterator> v3(1000);
How elements of these 3 vectors initialized?
About int, I test it and I saw that all elements become 0. Is this standard? I believed that primitives remain undefined. I create a vector with 300000000 elements, give non-zero values, delete it and recreate it, to avoid OS memory clear for data safety. Elements of recreated vector were 0 too.
What about iterator? Is there a initial value (0) for default constructor or initial value remains undefined? When I check this, iterators point to 0, but this can be OS
When I create a special object to track constructors, I saw that for first object, vector run the default constructor and for all others it run the copy constructor. Is this standard?
Is there a way to completely avoid initialization of elements? Or I must create my own vector? (Oh my God, I always say NOT ANOTHER VECTOR IMPLEMENTATION)
I ask because I use ultra huge sparse matrices with parallel processing, so I cannot use push_back() and of course I don't want useless initialization, when later I will change the value.
You are using this constructor (for std::vector<>):
explicit vector (size_type n, const T& value= T(), const Allocator& = Allocator());
Which has the following documentation:
Repetitive sequence constructor: Initializes the vector with its content set to a repetition, n times, of copies of value.
Since you do not specify the value it takes the default-value of the parameter, T(), which is int in your case, so all elements will be 0
They are default initialized.
About int, I test it and I saw that all elements become 0. Is this standard? I believed that primitives remain undefined.
No, an uninitialized int has an indeterminate value. These are default initialized, i.e.,
int i; // uninitialized, indeterminate value
int k = int(); // default initialized, value == 0
In C++11 the specification for the constructor vector::vector(size_type n) says that n elements are default-inserted. This is being defined as an element initialized by the expression allocator_traits<Allocator>::construct(m, p) (where m is of the allocator type and p a pointer to the type stored in the container). For the default allocator this expression is ::new (static_cast<void*>(p)) T() (see 20.6.8.2). This value-initializes each element.
The elements of a vector are default initialized, which in the case of POD types means zero initialized. There's no way to avoid it with a standard vector.