Skip to content

← Values, Types, Initialisation step 4 of 4

Medium Primitives

Update elements through references

This loop changes a copy of each Word, leaving the original counts unchanged:

for (auto word : words) {
    word.count += 1;
}

For a std::vector<Word>, plain auto declares a new Word initialized from the current element. To change that element, bind a reference with auto&.

How the declaration affects deduction

std::string name = "Ada";
const std::string& ref = name;

auto a = ref;         // std::string: a mutable copy
auto& b = ref;        // const std::string&: refers to name
const auto& c = ref;  // const std::string&: refers to name
auto&& d = ref;       // const std::string&: ref is an lvalue

In auto a = ref, deduction drops the reference and top-level constness. Top-level constness applies to the object itself. It is different from the constness of something reached through a pointer:

const int number = 7;
const int* const pointer = &number;
auto copy = pointer;  // const int*: the pointer can change, the int cannot

In auto& b = ref, the declared reference preserves the source object's constness. auto&& can bind to lvalues or rvalues; with this lvalue source, reference collapsing produces an lvalue reference. It does not make a const source mutable.

Choose the form for the element and the operation

Form Typical use
auto& Change an ordinary mutable element in place
const auto& Read an ordinary object without copying it
auto Work with a value, such as a cheap scalar or an intentional copy
auto&& Generic iteration over ranges that may yield references or temporary proxies

Copying a string or vector copies its contents and may allocate. Short strings can use internal storage, so an allocation per iteration is not guaranteed. Copying a small scalar can be simpler than referring to it.

Plain auto does not guarantee independent underlying data. A copied pointer still points to the same object, and a copied view still observes its source. Some containers return a proxy, an object that acts like a reference:

std::vector<bool> flags{false};
auto flag = flags[0];  // a proxy for the stored bit, not a bool copy
flag = true;          // changes flags[0]
bool snapshot = flags[0];  // an independent bool value

The exercise uses ordinary Word elements, so auto& gives direct access to each original object. References must remain valid while used; choosing a reference declaration does not by itself solve lifetime issues.

Your task

struct Word {
    std::string text;
    int count;
};

void tally(std::vector<Word>& words, const std::string& target);

Increment the count of every word whose text exactly equals target. Matching is case-sensitive, and an empty string can match an empty target. Preserve the text, element order and nonmatching counts. Matching counts in the tests can be incremented without overflowing int.

For words [("a", 0), ("b", 7), ("a", 3)] and target "a", the final counts should be [1, 7, 4]. With no matches or no words, change nothing.

The starter modifies copies. Fix the loop declaration so it updates the elements in place. The harness checks the counts after the call.