Implementation defined to use a reserved vector without resizing it? - c++

Is it implementation defined to use a reserved vector without resizing it?
By that I mean:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<unsigned int> foo;
foo.reserve(1024);
foo[0] = 10;
std::cout<<foo[0];
return 0;
}
In the above, I reserve a good amount of space and I assigned a value to one of the indices in that space. However, I did not call push_back which "resizes" the vector and gives it a default value for each element (which I'm trying to avoid). So in this foo.size() is 0 while foo.capacity() is 1024.
So is this valid code or is it implementation defined? Seeing as I'm assigning to a vector with "0" size. It works but I'm not sure if it's a good idea..
The reason I'm trying to avoid the default value is because for large allocations, I don't need it "zero-ing" out each index as I will decide when I want to write to it or not. I'd use a raw pointer but the lodepng API accepts only a vector for decoding from file.

std::vector::reserve just reserves memory, so the next push_back does not have to allocate memory. It does not change the size of the vector.
If you want a vector with an initial size of 1024 elements, you can use the constructor to do that:
std::vector<unsigned int> foo(1024);
Note that if you create a vector with an initial size of e.g. 1024 elements, if you then do push_back you add an element, so the size of the vector increases to 1025 elements.

It is illegal, regardless of the type of item in the container or what seems to happen on a particular compiler. From 23.1.1/12 (Table 68) we learn that operator[] behaves like *(a.begin() + n). Since you haven't added any items to the container this is the same as accessing an iterator past end() which is undefined.

Related

Any way that we can prevent the breakdown between reference and object when resize a vector?

I was debugging an issue and realized that when a vector is resizing, the reference will not work anymore. To illustrate this point, below is the minimal code. The output is 0 instead of 1. Is there anyway that we can prevent this happen except reserving a large space for x?
#include <iostream>
#include <vector>
using namespace std;
vector<int> x{};
int main(){
x.reserve(1);
x.push_back(0);
int & y = x[0];
x.resize(10);
y=1;
cout << x[0] << endl;
return 0;
}
This is called invalidation and the only way you can prevent it is if you make sure that the vector capacity does not change.
x.reserve(10);
x.push_back(0);
int &y = x[0];
x.resize(10);
The only way I can think of is to use std::deque instead of std::vector.
The reason for suggesting std::deque is this (from cppreference):
The storage of a deque is automatically expanded and contracted as
needed. Expansion of a deque is cheaper than the expansion of a
std::vector because it does not involve copying of the existing
elements to a new memory location.
That line about not copying is really the answer to your question. It means that the objects remain where you placed them (in memory) as long as the deque is alive.
However, on the very next line it says:
On the other hand, deques typically have large minimal memory cost; a
deque holding just one element has to allocate its full internal array
(e.g. 8 times the object size on 64-bit libstdc++; 16 times the object
size or 4096 bytes, whichever is larger, on 64-bit libc++).
It's now up to you to decide which is better - higher initial memory cost or changing your program's logic not to require referencing the items in the vector like that. You might also want to consider std::set or std::unordered_set for quickly finding an object within the container
There are several choices:
Don't use a vector.
Don't keep a reference.
Create a "smart reference" class that tracks the vector and the index and so it will obtain the appropriate object even if the vector moves.
You can create a vector of std::shared_ptr<> as well and keep the values instead of the interators.

Safe to iterate over std::vector<some_container> while modifying <some_container>'s size?

Suppose I have a vector of some other container type. While iterating over the vector I change the size of the containers. Given that vectors try to remain contiguous in system memory, could the pointer arithmetic fail in loops like this? For example,
#include <stdlib.h>
#include <vector>
using namespace std;
int main(){
vector<vector<double> > vec_vec(4);
for (auto i=vec_vec.begin(); i!=vec_vec.end(); ++i){
for (double j=0; j<100; j+=1.0){
i->push_back(j)
};
};
return 0;
}
I've had no issues using code like this so far, but now I'm wondering if I just got lucky. Is this safe? Does it depend on the kind of container used inside the vector?
That's perfectly OK, you are not changing the outer vector. However there is no guarantee that all vectors will be contiguous in the memory. Each individual inner one will be, but don't expect that they are arranged one after the other in memory.
You are modifying the contents of the std::vector you are iterating over. No the vector you are iterating over. They are different things.
First one is safe. Second one wouldn't be safe due to eventual memory reallocations.
A vector is a fixed size management object (size,reserved, pointer) with its contiguous memory pointed to by pointer.
Thus you are not changing object's size

Filling a vector with out-of-order data in C++

I'd like to fill a vector with a (known at runtime) quantity of data, but the elements arrive in (index, value) pairs rather than in the original order. These indices are guaranteed to be unique (each index from 0 to n-1 appears exactly once) so I'd like to store them as follows:
vector<Foo> myVector;
myVector.reserve(n); //total size of data is known
myVector[i_0] = v_0; //data v_0 goes at index i_0 (not necessarily 0)
...
myVector[i_n_minus_1] = v_n_minus_1;
This seems to work fine for the most part; at the end of the code, all n elements are in their proper places in the vector. However, some of the vector functions don't quite work as intended:
...
cout << myVector.size(); //prints 0, not n!
It's important to me that functions like size() still work--I may want to check for example, if all the elements were actually inserted successfully by checking if size() == n. Am I initializing the vector wrong, and if so, how should I approach this otherwise?
myVector.reserve(n) just tells the vector to allocate enough storage for n elements, so that when you push_back new elements into the vector, the vector won't have to continually reallocate more storage -- it may have to do this more than once, because it doesn't know in advance how many elements you will insert. In other words you're helping out the vector implementation by telling it something it wouldn't otherwise know, and allowing it to be more efficient.
But reserve doesn't actually make the vector be n long. The vector is empty, and in fact statements like myVector[0] = something are illegal, because the vector is of size 0: on my implementation I get an assertion failure, "vector subscript out of range". This is on Visual C++ 2012, but I think that gcc is similar.
To create a vector of the required length simply do
vector<Foo> myVector(n);
and forget about the reserve.
(As noted in the comment you an also call resize to set the vector size, but in your case it's simpler to pass the size as the constructor parameter.)
You need to call myVector.resize(n) to set (change) the size of the vector. calling reserve doesn't actually resize the vector, it just makes it so you can later resize without reallocating memory. Writing past the end of the vector (as you are doing here -- the vector size is still 0 when you write to it) is undefined behavior.

std::copy and std::vector problem

I understand why this causes a segfault:
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
int iArr[5] = {1, 2, 3, 4, 5};
int *p = iArr;
copy(p, p+5, v.begin());
return 0;
}
But why does this not cause a segfault?
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
int iArr[5] = {1, 2, 3, 4, 5};
int *p = iArr;
v.reserve(1);
copy(p, p+5, v.begin());
return 0;
}
Both are wrong as you are copying to empty vector and copy requires that you have space for insertion. It does not resize container by itself. What you probably need here is back_insert_iterator and back_inserter:
copy(p, p+5, back_inserter(v));
This is undefined behavior - reserve() allocates a buffer for at least one element and the element is left uninitialized.
So either the buffer is big enough and so you technically can access elements beyond the first one or it is not big enough and you just happen to not observe any problems.
The bottom line is - don't do it. Only access elements that are legally stored in the vector instance.
But why does this not cause a
segfault?
Because the stars aligned. Or you were running in debug and the compiler did something to "help" you. Bottom line is you're doing the wrong thing, and crossed over in to the dark and nondeterministic world of Undefined Behavior. You reserve one spot in the vector and then try to cram 5 elements in to the reserve-ed space. Bad.
You have 3 options. In my personal order of preference:
1) Use a back_insert_iterator which is designed for just this purpose. It is provided by #include <iterator>. The syntax is a bit funky, but fortunately a nice sugar-coated shortcut, back_inserter is also provided:
#include <iterator>
// ...
copy( p, p+5, back_inserter(v) );
2) assign the elements to the vector. I prefer this method slightly less simply because assign is a member of vector, and that strikes me as slightly less generic than using somethign from algorithm.
v.assign(p, p+5);
3) reserve the right number of elements, then copy them. I consider this to be a last ditch effort in case everything else fails for whatever reason. It relies on the fact that a vector's storage is contiguous so it's not generic, and it just feels like a back-door method of getting the data in to the vector.
This is wrong! it is undefined behavior to access memory you don't own, even if it works in an example. The reason, I think, is that std::vector would reserve more than one element.
Because you were unlucky. Accessing memory not allocated is UB.
Most likely because an empty vector doesn't have any memory allocated at all, so you are trying to write to a NULL pointer which normally leads to an instant crash. In the second case it has at least some memory allocated, and you are most likely overwriting the end of an array which may or may not lead to a crash in C++.
Both are wrong.
It would be wrong, by the way, even to copy 1 element to the vector that way (or to reserve 5 then copy that way).
The reason it most likely does not segfault is that the implementor felt it would be inefficient to allocate the memory for just 1 element just in case you wanted to grow it later, so maybe they allocated enough for 16 or 32 elements.
Doing reserve(5) first then writing into 5 elements directly would probably not be Undefined Behaviour but would be incorrect because vector will not have a logical size of 5 yet and the copy would almost be "wasted" as vector will claim to still have the size of 0.
What would be valid behaviour is reserve(5), insert an element, store its iterator somewhere, insert 4 more elements and look at the contents of the first iterator. reserve() guarantees that the iterators do not become invalidated until the vector exceeds that size or a call such as erase(), clear(), resize() or another reserve() is made.

Stumped at a simple segmentation fault. C++

Could somebody be kind to explain why in the world this gives me a segmentation fault error?
#include <vector>
#include <iostream>
using namespace std;
vector <double>freqnote;
int main(){
freqnote[0] = 16.35;
cout << freqnote[0];
return 0;
}
I had other vectors in the code and this is the only vector that seems to be giving me trouble.
I changed it to vector<int>freqnote; and changed the value to 16 and I STILL get the segmentation fault. What is going on?
I have other vector ints and they give me correct results.
Replace
freqnote[0] = 16.35;
with
freqnote.push_back(16.35);
and you'll be fine.
The error is due to that index being out-of-range. At the time of your accessing the first element via [0], the vector likely has a capacity of 0. push_back(), on the other hand, will expand the vector's capacity (if necessary).
You can't initialise an element in a vector like that.
You have to go:
freqnote.push_back(16.35),
then access it as you would an array
You're accessing vector out of bounds. First you need to initialize vector specifying it's size.
int main() {
vector<int> v(10);
v[0] = 10;
}
As has been said, it's an issue about inserting an out of range index in the vector.
A vector is a dynamically sized array, it begins with a size of 0 and you can then extend/shrink it at your heart content.
There are 2 ways of accessing a vector element by index:
vector::operator[](size_t) (Experts only)
vector::at(size_t)
(I dispensed with the const overloads)
Both have the same semantics, however the second is "secured" in the sense that it will perform bounds checking and throw a std::out_of_range exception in case you're off bound.
I would warmly recommend performing ALL accesses using at.
The performance penalty can be shrugged off for most use cases. The operator[] should only be used by experts, after they have profiled the code and this spot proved to be a bottleneck.
Now, for inserting new elements in the vector you have several alternatives:
push_back will append an element
insert will insert the element in front of the element pointed to by the iterator
Depending on the semantics you wish for, both are to be considered. And of course, both will make the vector grow appropriately.
Finally, you can also define the size explicitly:
vector(size_t n, T const& t = T()) is an overload of the constructor which lets you specify the size
resize(size_t n, T const& t = T()) allows you to resize the vector, appending new elements if it gets bigger than it was
Both method allow you to supply an element to be copied (exemplar) and default to copying a default constructed object (0 if T is an int) if you don't supply the exemplar explicitly.
Besides using push_back() to store new elements, you can also call resize() once before you start using the vector to specify the number of elements it contains. This is very similar to allocating an array.