Skip to content

← Copy, Move, and the Rule of Zero step 1 of 5

Easy Primitives

Read payloads without copying their contents

A declaration can create an independent copy even when the code only needs to read an existing object. In this exercise, Payload contains a vector of integers, so copying a Payload also copies that vector's contents.

The starter does this in two places:

for (Payload item : items) {
    std::vector<int> data = item.data;
    // Read data.
}

Each iteration constructs a Payload from an element of items. It then copies the copied Payload's data member into another vector. These are unnecessary here because the function never needs to modify or retain either copy.

Borrow the objects you only read

Bind references instead:

for (const Payload& item : items) {
    const std::vector<int>& data = item.data;
    // Read the original data through a const reference.
}

The references name existing objects; they do not duplicate the Payload or its vector. The caller's items and their contents must remain alive while those references are used. This function uses them only during the call.

Copying is not inherently wrong. A copy can provide an independent snapshot, and copying a small value such as int is often appropriate. The cost depends on the type: a vector copy duplicates its elements, whereas copying a pointer copies its address, not the object it points to. Avoid assuming every copy allocates or has a fixed byte cost.

A by-value parameter also does not always copy its caller's object:

void handle(std::string name);

Passing an existing string lvalue copies it, passing a suitable rvalue can move it, and a temporary may construct the parameter directly. Choose value or reference parameters according to ownership and lifetime needs.

Your task

Implement:

std::vector<int> heads(const std::vector<Payload>& items);

Return the first integer from each non-empty Payload, in input order. Skip empty payloads and leave all input data unchanged. For [[1, 2], [], [-3], [0, 4]], return [1, -3, 0]. Empty input or input containing only empty payloads returns an empty vector. Repeated values remain repeated.

Keep Payload and its instrumentation unchanged. Avoid both copying Payload objects and making temporary copies of their data vectors just to read them. Check empty() before front() or index 0; neither operation is valid on an empty vector in this C++20 exercise.

What the copy counter measures

The harness resets Payload::copies after building the inputs and reports [heads, copies]. For the worked example, expect [[1, -3, 0], 0].

The counter increments in Payload's copy constructor and copy assignment operator. In the starter, it records one Payload copy per input item, including empty payloads. The separate copy of item.data does not increment this counter, because it is a vector copy rather than a Payload copy.

A zero counter therefore does not prove that no copying or allocation occurred. The result vector still stores copies of the selected integers and may allocate memory. The goal is to avoid duplicating the input payloads and their vectors.

Clang-tidy checks such as performance-for-range-copy and performance-unnecessary-copy-initialization can identify unnecessary copies. Treat their diagnostics as guidance about specific declarations, not a complete measurement of runtime cost.