C++: How to properly go through vector pointed to - c++

Say that I have a vector, v,
vector <int> v = {0,1,2,...n};
What is the proper way to loop through v in a function taking a pointer to v as an argument? I can see two ways, either dereference the vector using (*) or using the .at() member:
void foo(vector <int>* v)
{
for (auto& el : (*v))
bar(el);
}
or
void foo(vector <int>* v)
{
for (int k = 0; k < n; k++)
bar(v->at(k));
}
Is there any difference between the two? Or is there another, superior, way to do it? It seems to me the second one would dereference the full vector object each iteration to get a single value, which seems like a bit of an over-kill, surely it would be better to only dereference the value at the memory location you want?

Pointer will be dereferenced in both cases. However at performs range checking and throws an exception if it fails so second approach will be slower.
A proper approach may look like this:
void foo(::std::vector<int> & v)
{
for(auto & el: v)
{
bar(el);
}
}
Since your code assumes that pointer is always valid there is not a single reason to use it instead of a reference. See Cpp Core Guidelines.

Related

Access an element of a vector given the vector's pointer

I am trying to create a sorting function with the parameters being a pointer of a list and I am trying to access an element of the given list. Hopefully this code speaks for the problem better than I can:
void bubbleSort(std::vector<int> *L) {
unsigned int i = 0; int temp;
while(isSorted(*L)) {
if(i==L->size()-1) {
i = 0;
}
if(i<L[i]/*<-ERROR here.*/) {
temp = L[i+1]; // ERROR HERE
L[i+1] = L[i]; // ERROR HERE
L[i] = temp; // ERROR HERE
}
}
}
You don't need to painfully dereference every individual use of L (and indeed doing so is error-prone, as you've demonstrated by missing one in your answer).
Instead, just write:
void bubbleSort(std::vector<int> *Lptr) {
auto &L = *Lptr;
and keep the rest of the code the same.
NB. It would be even better to change the function itself, to
void bubbleSort(std::vector<int> &L) {
as it should have been written in the first place, but I'm assuming there's some artificial reason you can't do that.
The function accepts a pointer to an object of type std::vector<int>.
void bubbleSort(std::vector<int> *L) {
To access the original vector using the pointer, you can write either *L or L[0]. That is, both expressions yield an lvalue reference of type std::vector<int> & to the vector.
To get the i-th element of the vector using the subscript operator through the pointer, you can write either (*L)[i] or L[0][i],
However, in this if statement:
if(i<L[i]/*<-ERROR here.*/) {
You are trying to compare the variable i of type unsigned int to the object L[i] of type std::vector<int>. When i is not equal to 0, this yields a non-existent object of the vector type.
It seems you mean something like the following instead:
if ( (*L)[i] < (*L)[i+1] ) {
or:
if ( L[0][i] < L[0][i+1] ) {
or, vice versa:
if ( L[0][i+1] < L[0][i] ) {
Depending on whether the vector is sorted in ascending or descending order.
Pay attention to the fact that there is no sense in declaring the parameter as a pointer to a std::vector<int>. The function would be much clearer and readable if it accepted the vector by reference instead:
void bubbleSort(std::vector<int> &L) {
In this case, the if statement would look like this:
if ( L[i] < L[i+1] ) {
Although I prefer to change the source code as other answer. But, for this question, you can use ->at() function to access the element in a vector pointer.
if(i<L->at(i)) {
temp = L->at(i+1);
L->at(i+1) = L->at(i);
L->at(i) = temp;
}

Create a vector of pairs, where the second element of a pair points to the next pair in the vector

I need to create a vector or similar list of pairs, where the first element of a pair is of class T, and the second element is a pointer to the next pair.
Illustration
template<class T>
std::vector<std::pair<T, T*>> createPointingVector(std::vector<T> vec) {
std::vector<std::pair<T, T*>> new_vec;
for (int i=0; i<vec.size(); i++){
new_vec.push_back(std::make_pair(vec[i], &(vec[i - 1])));
}
return new_vec;
}
I understand that std::vector<std::pair<T, T*>> is incorrect because the second element of the pair is not supposed to be of type *T but rather a recursive *std::pair<T, *std::pair<T, *std::pair<T, ...>>>.
Is it possible to fix my code or what are the other ways to achieve my goal, if any?
I strongly recommend rethinking using a bare vector.
My reason for that is that you need to guarantee that that the memory of the vector is never reallocated. Note that you also should in any case make sure that your vector is made sure to allocate all required memory from the start, either by initializing with empty elements or by using std::vector::reserve.
Otherwise, if you have a pointer already set and then change the capacity of the vector, the pointer becomes invalid, a good setup if you want undefined behaviour.
Therefore I strongly advise you to use a wrapper class around your vector, which makes sure no capacity change is ever called.
Now, if you do that, the thing is, why do you use actual pointers?
Consider using data of type std::vector<std::pair<T, size_t> >, with the second entry actually storing the position within the vector, rather than an actual pointer:
template<class T>
class PointingVector
{
public:
PointingVector(const std::vector<T>& vec);
private:
std::vector<std::pair<T, size_t> > data;
};
template<class T>
PointingVector<T>::PointingVector(const std::vector<T>& vec)
{
for (int i=0; i<vec.size()-1; i++)
{
data.push_back(std::make_pair(vec[i], i+1));
}
data.push_back(std::make_pair(vec.back(), 0)); // assuming that the last points to the first
}
After that, make sure that every additional method you add leaves the pointing consistent. Like should you write something similar to erase, make sure that all pairs are updated accordingly.
And the analogy to dereferencing is trivial:
template<class T>
std::pair<T, size_t>& PointingVector<T>::get(size_t index)
{
return data[index];
}
The important thing about my solution is that you can exclude possible bugs in regard to dangling pointers. Those are really bad, especially since they might not cause an error in test executions, given the nature of undefined behaviour. Worst thing in my solution is that the indices are wrong after calling a method that has a bug.
And if you want to introduce anything that changes the capacity of the vector, no problem, no need to redo any pointers. Just make sure the indices are changed accordingly. If you did this with pointers, your first step would probably be to create a list of indices anyway, so why not work with one directly.
Plus, as this solution has no (visible) pointers at all, you don't need to do any memory management.
Another solution: Ditch std::pair and define your own type:
template<class T>
struct Node
{
T data;
Node* next; // or a smart pointer type
Node(const T& data, Node* next) : data(data), next(next) {}
};
Then build up your vector like this:
template<class T>
std::vector<Node<T>*> createPointingVector(const std::vector<T>& vec)
{
std::vector<Node<T>*> new_vec;
for (int i=0; i<vec.size(); i++)
{
new_vec.push_back(new Node<T>(vec[i], nullptr));
}
for (int i=0; i<vec.size()-1; i++)
{
new_vec[i]->next = new_vec[i+1];
}
new_vec[vec.size()-1]->next = new_vec[0];
return new_vec;
}
Note that without smart pointers, you need to do memory management. I'd consider making next a weak_ptr<Node>, and have the vector be over shared_ptr<Node>. That way, the memory is automatically deallocated as soon as the vector gets deleted (assuming you have no other pointers active).
What you ask is doable, but according to the illustration found linked within your answer, the pointers should point one-up circularly inside the input vector, and not one-down, as is in your code example. What I mean is:
new_vec[0] = {vec[0], &vec[1]}
new_vec[1] = {vec[1], &vec[2]}
...
new_vec[N-1] = {vec[N-1], &vec[0]}
above, N = vec.size().
I attach a minimum working example:
#include <iostream>
#include <vector>
#include <utility> // std::pair, std::make_pair
template<class T>
std::vector<std::pair<T, T*> > createPointingVector(std::vector<T>& vec) { // important: make the parameter a reference
std::vector<std::pair<T, T*> > new_vec;
int vec_size = vec.size();
for (int i = 0; i < vec_size-1; i++)
new_vec.push_back( std::make_pair( vec[i], &(vec[i + 1]) ) ); // pointers assigned according to linked picture
new_vec.push_back( std::make_pair( vec[vec_size-1], &vec[0] ) );
return new_vec;
}
int main()
{
std::vector<int> input = {1,2,3,4};
std::vector<std::pair<int,int*> > sol = createPointingVector(input);
for (auto i : sol)
std::cout << i.first << " -> " << *(i.second) << std::endl;
return 0;
}

Why is vector::clear not working inside foreach loop?

My code:
vector<int> v[10];
const int x = 3;
void clearBySimpleLoop(){
for (int i = 0; i < x; i++){
v[i].clear();
}
}
int main()
{
for (int i = 0; i < x; i++){
v[i].push_back(11+i);
v[i].push_back(11+i+1);
v[i].push_back(11+i+2);
}
for (auto vec : v) vec.clear(); //#01. Contents are not cleared. Size of the contained vectors remains same.
clearBySimpleLoop(); //#02. Contents are cleared. Size of the contained vectors becomes zero.
return 0;
}
Question is why does the code inside the foreach loop (#01) fails to clear the vector in the array, while a simple for loop (#02) does it successfully?
Demo: https://onlinegdb.com/B1m8-2jG4
When you write
for (auto vec : v) vec.clear(); //
then auto gets deduced as std::vector<int>, hence vec are copies of the elements of v. You clear the copies, but leave the actual elements unchanged. If you want to operate on the elements themself you have to use references:
for (auto& vec : v) vec.clear();
My personal rule of thumb is to always mention pointerness, constness and referenceness explicitly when using auto. I think it makes usages of auto much more readable, but thats just my opinion. Note that here you have no choice, but if you'd follow the rule you could have realized more easily that vec are values, not references.

How to edit an object's data from a function?

I apologize if this question has already been answered (I tried searching around, but couldn't find anything quite the same, and similar questions' solutions didn't work), but how do I pass an object (in this case, a vector of objects) and have the function edit those values without returning anything?
Example:
void incVector(std::vector<int> vec)
{
for (auto l = 0; l < int(vec.size()); l++)
{
vec[l]++;
}
}
int main()
{
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
for (auto l = 0; l < int(vec.size()); l++)
{
std::cout << vec[l]; //Output: "12"
}
incVector(vec);
for (auto l = 0; l < int(vec.size()); l++)
{
std::cout << vec[l]; //Output: "12"
//This output should be "23"
}
}
Obviously, what I'm actually using this for is much more complex, but this is enough of an example to get the point of what I'm trying to do across. In the actual project, it's a rabbit hole of different functions, some of which return things while others don't, so having it simply return the vector isn't an option.
I have tried making incVector accept a reference to a vector, a pointer to a vector, a pointer to a reference to a vector, and a pointer to a pointer to a vector (which are solutions that seemed to work for other similar questions) but none of those are working for me.
EDIT:
God, I feel stupid. I swear I'd tried using a reference before and it didn't work. Yet now, trying it again works just fine. Sorry! ^^;
you pass the vector by value, thus modifications are purely local:
change prototype of your function to:
void incVector(std::vector<int> &vec)
to pass by reference and get the one from main modified
Take the argument by reference. You can also use a modern for-loop.
void incVector(std::vector<int>& vec)
{
for (auto& l : vec)
{
++l;
}
}
But you don't actually need to do any of this. Applying an operation on each element of the vector can be done easily using a standard algorithm (std::for_each) and a lambda function that takes a reference to the vector's element:
#include <algorithm>
// ...
std::for_each(vec.begin(), vec.end(), [](int& l){ ++l; });
for_each is going to call the lambda for each element in the vector and pass it as the argument. Since you take the argument by reference (int&), incrementing it will increment the actual element contained in the vector.
The key point to take away from this however, is that when you want to give a new name to an object that already exists, you use a reference. Those are declared with & before their identifier:
int i = 0;
int &i_ref = i;
Here, i_ref is a reference to i, meaning it's just another name for i. Modifying i_ref is the same as modifying i.
The same applies to function arguments. If a function argument is a reference, it means it's another name for the object that was passed to the function. Modifying the reference is the same as modifying the object that was passed to the function.

const_cast issue with std::map

I recently hit a problem and the only way I can see to avoid it is to use const_cast - but I'm guessing there is a way I'm not thinking of to avoid this without otherwise changing the function of the code. The code snippet below distills my problem into a very simple example.
struct Nu
{
Nu() {v = rand();}
int v;
};
struct G
{
~G()
{
for(auto it = _m.begin(); it != _m.end(); it++) delete it->first;
}
void AddNewNu()
{
_m[new Nu] = 0.5f;
}
void ModifyAllNu()
{
for(auto it = _m.begin(); it != _m.end(); it++) it->first->v++;
}
float F(const Nu *n) const
{
auto it = _m.find(n);
// maybe do other stuff with it
return it->second;
}
map<Nu*, float> _m;
};
Here, suppose Nu is actually a very large struct whose layout is already fixed by the need to match an external library (and thus the "float" can't simply be folded into Nu, and for various other reasons it can't be map<Nu, float>). The G struct has a map that it uses to hold all the Nu's it creates (and ultimately to delete them all on destruction). As written, the function F will not compile - it cannot cast (const Nu *n) to (Nu n) as expected by std::map. However, the map can't be switched to map<const Nu*, float> because some non-const functions still need to modify the Nu's inside _m. Of course, I could alternatively store all these Nu's in an additional std::vector and then switch the map type to be const - but this introduces a vector that should be entirely unnecessary. So the only alternative I've thought of at the moment is to use const_cast inside the F function (which should be a safe const_cast) and I'm wondering if this is avoidable.
After a bit more hunting this exact same problem has already been addressed here: Calling map::find with a const argument
This is because the map expects Nu* const, but you have given it a const Nu*. I also find it highly illogical and don't understand why, but this is how it is.
"find" in your case will return a const_iterator. putting:
map<Nu*,float>::const_iterator it = _m.find(n);
...
return it->second;
should work I think.
Since you are in a const method you can only read your map of course, not write/modify it