Using vector::back() to modify vector element - c++

I have the following struct:
#include <string>
#include <vector>
struct A {
std::string name;
int id;
};
And a vector containing A elements:
std::vector<A> a_vector;
I am trying to append an element to the vector and change its values using the following:
void test()
{
A a;
get_a(a);
//Up to this point I thought modifying this a object would mean modifying the back element of the vector. But it doesn't work as planned, doing this:
a.id = 2; //Doesn't modify the id of the element in the vector.
}
where get_a is defined as : (The code is simplified, in the real one I really need to pass a as argument and not get it as return)
void get_a(A& a) //This function normally assigns a in different ways
{
a_vector.emplace_back();
a = a_vector.back();
}
How can I do to have the a element be the same as the one in the vector? Do I really have to use pointers?

A a;
a = a_vector.back();
Here you're copy-assigning a_vector.back() to a. This is not a reference, so modifying a will not modify the element inside the vector.
You want this instead:
A& a = a_vector.back();
If you cannot immediately initialize your reference with a_vector.back(), consider using a pointer...
A* a;
// ...
a = &a_vector.back();
// ...
something(*a);
...or an index:
std::size_t a_idx;
// ...
a_idx = a_vector.size() - 1;
// ...
something(a_vector[a_idx]);
The pointer will work fine if you know that the vector won't get resized. If the vector resize, iterators and pointers will be invalidated.
The index will work fine even if the vector gets resized, as long as the elements are not removed/shifted around.

You need a reference to the object:
auto& a = a_vector.back();
Or, in a more compact manner:
a_vector.back().id = 2;
You're holding a copy, not the original object. That is why the object in vector does not get modified.
Answer to edited question: references can be assigned only during declaration. What you want is probably std::reference_wrapper, but anyway, please don't use it unless you have to.

Related

Passing std::vector::data to function expecting type** (double pointer)

As the title describes, I am trying to pass the pointer to the data of a std::vector into a function expecting a double pointer. Take as an example the code below. I have an int pointer d which is passed to myfunc1 as &d (still not sure if call it the pointer's reference or what), where the function changes its reference to the beginning of an int array filled with 1,2,3,4. However, if I have a std::vector of ints and try to pass &(vec.data()) to myfunc1 the compiler throws the error lvalue required as unary ‘&’ operand. I have already tried something like (int *)&(vec.data()) as per this answer, but it does not work.
Just for reference, I know I can do something like myfunc2 where I directly pass the vector as reference and the job is done. But I want to know if it's possible to use myfunc1 with the std::vector's pointer.
Any help will be very much appreciated.
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
void myfunc1(int** ptr)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
*ptr = values;
}
void myfunc2(vector<int> &vec)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
vec.assign(values,values+4);
delete values;
}
int main()
{
// Create int pointer
int* d;
// This works. Reference of d pointing to the array
myfunc1(&d);
// Print values
for(auto& i:{0,1,2,3})
{
cout << d[i] << " ";
}
cout << endl;
// Creates the vector
vector<int> vec;
// This works. Data pointer of std::vector pointing to the array
myfunc2(vec);
// Print values
for (const auto &element : vec) cout << element << " ";
cout << endl;
// This does not work
vector<int> vec2;
vec2.resize(4);
myfunc1(&(vec2.data()));
// Print values
for (const auto &element : vec2) cout << element << " ";
cout << endl;
return 0;
}
EDIT: What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector. I was having troubles getting the modified vector out of a read function, and this is what I came up with that allowed me to solve it.
When you write:
myfunc1(&(vec2.data()));
You are getting the address of a rvalue. The pointed int* is so a temporary that is destroyed right after the call.
This is why you get this error.
But, as #molbdnilo said, in your myfunc1() function, you are reassigning the pointer (without caring to destroy previously allocated memory by the way).
But the std::vector already manages its data memory on its own. You cannot and you must not put your hands on it.
What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector.
A solution could be to construct your std::vector by passing the iterator to the beginning and the iterator to the end of the desired part to extract in the constructor's parameters.
For example:
int * buffer = readAll("path/to/my/file"); // Let's assume the readAll() function exists for this example
// If you want to extract from element 5 to element 9 of the buffer
std::vector<int> vec(buffer+5, buffer+9);
If the std::vector already exists, you can use the assign() member function as you already did in myfunc2():
vec.assign(buffer+5, buffer+9);
Of course in both cases, you have to ensure that you are not trying to access an out of bounds element when accessing the buffer.
The problem is that you cannot take the address of data(), since it is only a temporary copy of the pointer, so writing to a pointer to it makes not that much sense. And that is good that way. You DO NOT want to pass data() to this function since it would overwrite the pointer with a new array and that would break the vector. You can remove one * from the function and only assign to it and not allocate the memory there. This will work, but make sure to allocate the memory in the caller (with resize, just reserve will result un undefined behavior, since data() is only a pointer to the beginning of the valid range [data(), data() + size()). The range [data(), data() + capacity ()) is not necessary valid.

Can't explain why pointers become dangling when creating a vector of vectors of pointers

Consider the following code:
#include <iostream>
#include <vector>
using namespace std;
class SomeClass {
public:
SomeClass(int num) : val_(num) {}
int val_;
int val() const { return val_; }
};
// Given a vector of vector of numbers, this class will generate a vector of vector of pointers
// that point to SomeClass.
class Generator {
public:
vector<SomeClass> objects_;
vector<vector<SomeClass*> > Generate(const vector<vector<int> >& input) {
vector<vector<SomeClass*> > out;
for (const auto& vec : input) {
out.push_back({});
for (const int num : vec) {
SomeClass s(num);
objects_.push_back(s);
out.back().push_back(&objects_.back());
}
}
return out;
}
};
int main() {
Generator generator;
auto output = generator.Generate({{2, 3}, {4, 5}, {6}});
for (const auto& vec : output) {
for (const auto* obj : vec) {
printf("%d ",obj->val());
}
printf("\n");
}
return 0;
}
The Generate method in the Generator class will simply convert the vector of vector of ints to a vector of vector of pointers to SomeClass.
SomeClass is simply a container for a simple int value with a getter method.
I would expect the following output:
2 3
4 5
6
However, I get the following output:
junk_integer junk_integer
4 5
6
It seems the pointers in the first row become dangling pointers. What is wrong with this code?
You're storing pointers into a vector, then adding elements to the vector. Since you're not reserving enough space for all the elements you're adding, when the vector resizes it invalidates all the pointers to the old data.
You'll either have to reserve enough space before storing the pointers, store the pointers after you've stored everything in the vector you need to store, or not store pointers (maybe store an index, instead).
All operations that increase the number of elements in a std::vector, including push_back(), invalidates all iterators (including pointers) that refer to elements of the vector, if the resizing results in a change of vector capacity.
Your code is doing
objects_.push_back(s);
out.back().push_back(&objects_.back());
within a loop. Every call of objects_.push_back() invalidates iterators of objects_, and therefore can result in out.back() containing invalid (dangling) pointers.
You are storing pointers to objects contained in generator.objects_. Some of them become dangling pointers when you call push_back() on that.
In general, storing pointers to objects in a std::vector is a bad idea.

Dereferencing not working for std::vector<std::vector<double> >

struct myS {
std::vector<std::vector<double> > *A;
}
I want to access the elements of A using indices. I tried this (and also other versions) but it did not work.
struct myS test;
std::vector<double> B = *(test.A)[0];
Reposition your brackets:
std::vector<double> B = (*test.A)[0];
This compiles:
struct myS{
std::vector<std::vector<double> > *A;
};
myS instanceOfMyS;
std::vector<double> B = (*instanceOfMyS.A)[0];
Note
struct myS just declares a type of myS. To declare an instance of the type you need to add the instanceOfMyS afterwords.
You also omitted the <double> in the declaration of B.
However the code above will take a copy of the first element in *instanceOfMyS.A. So you might want a reference instead.
std::vector<double>& B = (*instanceOfMyS.A)[0];
Finally, it looks a bit dodgy that you're using a pointer in your struct (with a pointer you don't allocate the memory to back the vector pointed to be A unless you explicitly allocate the memory, leading to access violations). You might want the following
struct myS{
std::vector<std::vector<double> > A;
};
myS instanceOfMyS;
std::vector<double>& B = instanceOfMyS.A[0];
This will work :
myS myObj;
// Add some stuff in your vector(s) ...
...
// Access your vector
std::vector<double> B = (*myObj.A)[0]; // Don't forget here that it will copy the elements of the vector
Or if you want to access to an item into the second vector :
double B = (*myS.A)[0][0];
Another question : Why are you using a vector in the structure in this case ? In my own opinion, you should not have this pointer in your struct. It should be a value like :
struct myS{
std::vector<std::vector<double> > A;
}
EDIT : small mistakes on dereferencing pointer resolved
You should write (myS->A)[i] to access the ith outer vector: rewrite your line as
std::vector B = (myS->A)[0];
but note that this will take a value copy of the vector.
By the way, you will need to do (myS->A)[i][j] to access the jth element in the ith outer vector,
But why use a pointer in the first place?

How to pass a vector to another vector push back? (without creating a extra variable to pass)

Well I am questioning myself if there is a way to pass a vector directly in a parameter, with that I mean, like this:
int xPOS = 5, yPOS = 6, zPOS = 2;
//^this is actually a struct but
//I simplified the code to this
std::vector <std::vector<int>> NodePoints;
NodePoints.push_back(
std::vector<int> {xPOS,yPOS,zPOS}
);
This code ofcourse gives an error; typename not allowed, and expected a ')'
I would have used a struct, but I have to pass the data to a Abstract Virtual Machine where I need to access the node positions as Array[index][index] like:
public GPS_WhenRouteIsCalculated(...)
{
for(new i = 0; i < amount_of_nodes; ++i)
{
printf("Point(%d)=NodeID(%d), Position(X;Y;Z):{%f;%f;%f}",i,node_id_array[i],NodePosition[i][0],NodePosition[i][1],NodePosition[i][2]);
}
return 1;
}
Ofcourse I could do it like this:
std::vector <std::vector<int>> NodePoints;//global
std::vector<int> x;//local
x.push_back(xPOS);
x.push_back(yPOS);
x.push_back(zPOS);
NodePoints.push_back(x);
or this:
std::vector <std::vector<int>> NodePoints;//global
std::vector<int> x;//global
x.push_back(xPOS);
x.push_back(yPOS);
x.push_back(zPOS);
NodePoints.push_back(x);
x.clear()
but then I'm wondering which of the two would be faster/more efficient/better?
Or is there a way to get my initial code working (first snippet)?
Use C++11, or something from boost for this (also you can use simple v.push_back({1,2,3}), vector will be constructed from initializer_list).
http://liveworkspace.org/code/m4kRJ$0
You can use boost::assign as well, if you have no C++11.
#include <vector>
#include <boost/assign/list_of.hpp>
using namespace boost::assign;
int main()
{
std::vector<std::vector<int>> v;
v.push_back(list_of(1)(2)(3));
}
http://liveworkspace.org/code/m4kRJ$5
and of course you can use old variant
int ptr[1,2,3];
v.push_back(std::vector<int>(ptr, ptr + sizeof(ptr) / sizeof(*ptr));
If you don't have access to either Boost or C++11 then you could consider quite a simple solution based around a class. By wrapping a vector to store your three points within a class with some simple access controls, you can create the flexibility you need. First create the class:
class NodePoint
{
public:
NodePoint( int a, int b, int c )
{
dim_.push_back( a );
dim_.push_back( b );
dim_.push_back( c );
}
int& operator[]( size_t i ){ return dim_[i]; }
private:
vector<int> dim_;
};
The important thing here is to encapsulate the vector as an aggregate of the object. The NodePoint can only be initialised by providing the three points. I've also provided operator[] to allow indexed access to the object. It can be used as follows:
NodePoint a(5, 6, 2);
cout << a[0] << " " << a[1] << " " << a[2] << endl;
Which prints:
5 6 2
Note that this will of course throw if an attempt is made to access an out of bounds index point but that's still better than a fixed array which would most likely seg fault. I don't see this as a perfect solution but it should get you reasonably safely to where you want to be.
If your main goal is to avoid unnecessary copies of vector<> then here how you should deal with it.
C++03
Insert an empty vector into the nested vector (e.g. Nodepoints) and then use std::swap() or std::vector::swap() upon it.
NodePoints.push_back(std::vector<int>()); // add an empty vector
std::swap(x, NodePoints.back()); // swaps contents of `x` and last element of `NodePoints`
So after the swap(), the contents of x will be transferred to NodePoints.back() without any copying.
C++11
Use std::move() to avoid extra copies
NodePoints.push_back(std::move(x)); // #include<utility>
Here is the explanation of std::move and here is an example.
Both of the above solutions have somewhat similar effect.

Move std::vector<T> to T*

all
I've a legacy code which in draft does something like this:
// sadly I have to use this structure
struct LegacyStruct {
int* values;
}
LegacyStruct* LgStr;
....
std::vector<int> vec;
// fill vector in some way here
size_t sz = vec.size();
LgStr->values = new int[sz];
std::copy(vec.begin(), vec.end(), &LgStr->values[0]);
vec can be huge and I need to avoid copying it to int*.
Is there a way to do it?
I tried following:
// type of new operator explained in More Effective C++
LgStr->values = new (&vec[0])int[vec.size()];
Ok, values points to the beginning of vec inner array, but it destroyed when vec is out of scope. But I have to keep it..
&vec[0] = nullptr; // does not compile of course
So question is: is it possible to apply move semantics in this case?
Or maybe some other trick?
The short answer is that no, there isn't any way to transfer ownership of a vector's buffer outside the vector.
I think your best option is to make sure that the vector just doesn't die by using a wrapper:
class LegacyStructWrapper : private boost::noncopyable // Or declare private copy constructor/copy assignment or use `= delete` in C++11.
{
private:
std::vector<int> vec_;
LegacyStruct wrapped_;
}
Then anytime you need to use values, just assign it to &vec_[0]. This will stay constant if/until you add more items to the vector (so you will have to use care to make sure that vector resizes don't cause problems).
Yup, you can do so - with a small trick:
struct LegacyStruct {
std::vector<int> backingStore;
int* values;
LegacyStruct(std::vector<int>& aSource) {
// Steal memory
aSource.swap(backingStore);
// Set pointer
values = &backingStore[0];
};
}
The vector.swap operation doesn't copy the ints.