Skip to content

← References, Pointers, Ownership step 1 of 4

Easy Primitives

Say it in the signature

In C++ a function’s parameter list is not just how arguments arrive. It is a contract with the caller about what will happen to their object, and it is checked by the compiler. Choosing the wrong one does not merely cost performance — it can make the function unable to do its job.

The table

What you intend Write Because
Read a cheap value int n, double d, T* p A reference to a register-sized thing costs more than the copy.
Read an expensive value const T& t No copy, and const promises you will not modify it.
Modify the caller’s object T& t The only one of these that can.
Take ownership T t, then std::move(t) The caller decides at the call site whether that is a copy or a move.
Refer to something that may be absent const T* p A reference cannot be null; a pointer can.

Almost all real code is the first three rows. The fourth is worth knowing because it looks like a mistake and is not.

Why “take ownership” is by value

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

It looks like the copy you were just taught to remove. It is not, because the caller chooses:

keep(sink, existing);              // copies — the caller keeps theirs
keep(sink, std::move(existing));   // moves  — the caller gave it up

One signature serves both, and the cost is exactly what the caller asked for. Writing const Doc& here would force a copy on both paths; writing two overloads — const Doc& and Doc&& — duplicates the body. This is the standard shape for a sink parameter, and performance-unnecessary-value-param knows about it: it does not fire on a by-value parameter that is moved from.

Why the wrong choice is not only slow

void add(Doc doc, int word);   // modifies a copy, then throws it away
void add(Doc& doc, int word);  // modifies the caller's

The first compiles, runs, and silently does nothing the caller can see. There is no diagnostic for “you meant the other one”; the tests are what catch it.

Your task

Three helpers are given, and process calls them. Fix the signatures. Do not change any function body, and do not change process.

int size_of(Doc doc);
void add(Doc doc, int word);
void keep(std::vector<Doc>& sink, Doc doc);

One of the three is already right. Doc counts its copies and the harness prints the count, so the answers and the cost are both graded.