Skip to content

← References, Pointers, Ownership step 1 of 4

Easy Primitives

Say it in the signature

A parameter declaration controls how a function receives an object. For this exercise's Doc, passing by value creates a separate document, while passing by reference gives access to the caller's document.

Purpose Typical declaration Effect
Read a small scalar int value Receive a cheap value copy
Read an existing Doc const Doc& doc Borrow it without copying its vector
Append to the caller's Doc Doc& doc Borrow it with permission to modify it
Receive a Doc to store Doc doc Own a parameter that can be moved into storage
Borrow an optional object const Doc* doc Accept a pointer, with nullptr representing absence

These are common choices, not universal cost guarantees. A copied pointer or view can still refer to caller-owned data. Constness restricts access through that parameter; it does not ensure that no other reference can change the same object. A non-null pointer must still be valid when dereferenced.

Reading and modifying need different access

int size_of(const Doc& doc);  // read the original without a Doc copy
void add(Doc& doc, int word); // append to the original

In the starter, both helpers take Doc by value. size_of copies the vector just to read its length. add appends to its local copy, which is destroyed when the function returns, so the original document receives no new word. A const reference avoids copying for the read. A mutable reference lets the unchanged append expression modify the original vector.

Receiving a document to keep

The third helper already uses a suitable by-value parameter:

void keep(std::vector<Doc>& sink, Doc doc) {
    sink.push_back(std::move(doc));
}

Here sink is the destination vector. For the provided copyable, movable Doc:

keep(sink, existing);             // copy into the parameter, then move into sink
keep(sink, std::move(existing));  // move into the parameter, then move into sink

The first call leaves existing unchanged. The second leaves it moved from; it still exists, but callers should not expect its former contents. Vector growth may also relocate elements already in sink.

A parameter consumed this way is often called a sink parameter. By value is one design: an rvalue-reference parameter or separate copy/move overloads can serve other interfaces. With this helper's unchanged body, switching to const Doc& would make the insertion copy because std::move preserves const.

Your task

Fix only the parameter declarations of size_of and add. Leave keep, all function bodies, Doc and process unchanged.

The provided process function:

  1. Appends every input word to every document through add.
  2. Records each document's updated length through size_of.
  3. Moves the documents into a local destination through keep.
  4. Appends the number of documents in that destination to the result.

For documents [[1], [2, 3]] and words [9], the result is [2, 3, 2]: two updated lengths, followed by two stored documents. With no documents, the result is [0]. Test lengths and document counts fit in int.

The harness also reports Doc::copies during process; it must be zero. It counts Doc copy operations, not allocations. Appending words or reserving vector storage can still allocate memory in a correct solution.