Home > OS >  Assigning to std::array element in std::vector of arrays fails
Assigning to std::array element in std::vector of arrays fails

Time:04-29

Today, I'm working on understanding some new-to-me features, particularly std::array and std::vector. Individually, these seem to behave as expected, but I'm very puzzled by the behavior illustrated below:

This version works:

printf("Using pointer:\n");
std::vector<std::array<int, 1>*> vap;
vap.push_back(new std::array<int, 1>());
printf("size of vector is %ld\n", vap.size());

printf("before setting value:\n");
printf("value is %d\n", vap.front()->at(0));
std::array<int, 1>* pvals = vap.front();
pvals->at(0) = -1;
printf("after setting value:\n");
printf("value is %d\n", vap.front()->at(0));

This version doesn't update the array:

printf("Without pointer:\n");
std::vector<std::array<int, 1>> va;
std::array<int, 1> arr = {99};
va.push_back(arr);

Inserting the array like this fails too: va.push_back(std::array<int, 1>());

printf("size of vector is %ld\n", va.size());

printf("before setting value:\n");
printf("value is %d\n", va.front().at(0));
std::array<int, 1> vals = va.front();
vals[0] = -1;
printf("after setting value:\n");
printf("value is %d\n", va.front().at(0));

It's likely obvious what I'm trying to do but, in case it helps, I'll write it in prose:

Loosely, I'm creating a vector of arrays of ints. In the first half of the example, I create a vector of pointers to those arrays, and am able to insert a new array, via a pointer, and then modify the element contained in that array. In the second half, I tried to avoid using pointers. That code seems to insert the array successfully but then does not allow me to alter the element within it.

I'm somewhat surprised that there are zero warnings or errors, either at compile or runtime, and I'm guessing that I'm missing something fundamental; can someone point me in the right direction?

CodePudding user response:

You are working on a copy of the array, you need a reference to the array in the vector

int main() {
    vector<array<int, 1>> v;
    array<int, 1> a = { 99 };
    v.push_back(a);
    cout << v[0][0];
    auto& ar = v[0];
    ar[0] = 42;
    cout << v[0][0];
}

gives

 99 42

CodePudding user response:

Both your va.push_back(arr); and std::array<int, 1> vals = va.front(); statements are making copies of their source operands. Thus, any changes made to those copies won't affect the source arrays.

In the case of the push_back, this is possibly what you want, so that you can (for example) use the same source variable (arr) – after suitable modifications – to push multiple (different) arrays into your vector.

However, in the second case, that copy is almost certainly not what you want. Instead, you should declare a reference variable and assign that to the element of the vector you want to modify.

In the trivial case that your have shown in your code, the usage would be as follows:

//...
std::array<int, 1>& vals = va.front(); // The "&" declares a reference
vals[0] = -1; // This now modifies the (element of the) array referred to.

But note, you can't reassign a reference variable after it has been declared and initialized as referring to a particular source; thus, a subsequent vals = va.at(2); would not do what you might think (it will, in fact, replace the previously referred-to element with a copy of the RHS array). A notable 'exception' to this is when using such a reference variable in a range-based for loop, like this:

for (auto& vals : va) {
   vals[0] = 42; // This will refer to a different "va" element each loop
}

You can, similarly, define such a reference explicitly inside a more traditional for loop (or, in fact, in any loop or block-scope); that will, effectively, create a new reference each time the loop is iterated (actually, this is also what is being done in the above, range-based loop). So, this code will set the first element of each array to the loop's i index:

for (size_t i = 0; i < va.size();   i) {
    auto& vals = va.at(i);
    vals[0] = static_cast<int>(i);
}
  • Related