Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions hints/02_functions/functions2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Hint

A function parameter needs both a type and a name. `callme(x)` declares `x`
with no type, which is not valid C++. `x` is later compared against `int i` and
used as a loop bound, so give it the matching type.
4 changes: 4 additions & 0 deletions hints/02_functions/functions3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Hint

`callme` is declared to take one argument, but it is called with none. Look at
its signature and pass the value it expects.
5 changes: 5 additions & 0 deletions hints/02_functions/functions4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Hint

`sale_price` is declared to return `void`, yet its body uses `return price - 10;`.
A function that hands a value back to its caller must declare a return type that
matches the value. Look at what the test expects `function_syntax()` to return.
8 changes: 8 additions & 0 deletions hints/02_functions/functions5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

Two things are broken:

- `is_even` is declared `constexpr void` but returns a `bool`. Give it the
return type that matches the value it computes.
- The lambda takes no parameters (`[]()`) yet its body uses `price`. A lambda
needs its inputs declared in the parameter list, just like a normal function.
11 changes: 11 additions & 0 deletions hints/02_functions/functions6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Hint

The return line must add three separate values, but the placeholders are empty.
Look at each scope in play:

- the global `value` (reachable with `::value`)
- the value returned by `called(10)`, stored in the local `value`
- the one inside `my_namespace_0` (reachable with `my_namespace_0::value`)

The expected total is `13`. Work out which value lives in which scope and add
the three named references together.
11 changes: 11 additions & 0 deletions hints/03_if/if1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Hint

The function body is empty, so nothing is returned. The constraints rule out
extra variables and helper calls, which points at the conditional (ternary)
operator:

```
condition ? value_if_true : value_if_false
```

Return that single expression, comparing `a` and `b`.
13 changes: 13 additions & 0 deletions hints/03_if/if2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Hint

Two problems. First, the function can reach its end without returning a value:
every path through the `if` chain must `return` a `std::string`.

Look at the test cases at the bottom for the expected mapping:

- `"fizz"` returns `"foo"`
- `"fuzz"` returns `"bar"`
- anything else returns `"baz"`

Add an `else if` for the `"fuzz"` case and a final `else` (or a trailing
`return`) for the default.
9 changes: 9 additions & 0 deletions hints/04_pointers_references/pointers_references1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`my_pointer_value_swap` receives an `int*` but stores it in an `int`. The local
`new_data` must have the same type as what it holds and what the function
returns: a pointer. Fix its type so it can carry the address through.

`my_pointer_content` takes `int * const value`: the pointer itself is const, so
you cannot move where it points. To change the pointed-to number, work through
the dereferenced value (`*value`) rather than the pointer.
8 changes: 8 additions & 0 deletions hints/04_pointers_references/pointers_references2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

The test expects the caller's value to change after `my_reference_content`
returns, but the parameter is passed by value, so the function only edits a
local copy. Line 14 must stay as-is.

Change the parameter that should be modified so it is a reference (`int&`)
instead of a copy. A reference is an alias for the caller's variable.
9 changes: 9 additions & 0 deletions hints/05_classes/classes1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`Bird` and `Cat` inherit `number_legs` and `has_fur` from `Animal`, so they
should set those inherited members, not declare new ones that shadow them.

- In the `Bird` constructor, `number_legs = ;` is missing its value. A bird has
two legs.
- `Cat` re-declares `has_fur` (line 41). Delete that duplicate so the
constructor writes the inherited member instead of a new shadowing one.
10 changes: 10 additions & 0 deletions hints/05_classes/classes2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Hint

Several holes to fill in this one:

- `operator==` compares two points. Mirror the `x` comparison for `y`: take the
absolute difference and check it is below the tolerance `1e-6`.
- `point_zero` is a `Point`, so it needs a brace-initialised value for its two
coordinates.

Look at how the `x` half of the comparison is written and follow the same shape.
9 changes: 9 additions & 0 deletions hints/05_classes/classes3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`Circle` derives from `Shape`, but `Shape` has no default constructor that runs
on its own here: the base part must be initialised explicitly. A derived
constructor forwards to its base in the member-initialiser list, before the
body runs.

Look up "constructor initializer list" and how a derived class calls its base
constructor, then pass the incoming centre through to `Shape`.
9 changes: 9 additions & 0 deletions hints/05_classes/classes4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

Virtual dispatch (the vtable) only happens through a reference or a pointer to
the base class, never through a by-value copy. Slicing a `Circle` into a
`Shape` value loses the derived behaviour.

`shape_collection` holds `Shape*`, so every element must be an address, and
each element is accessed as a pointer. Fix the type or the way the elements are
stored and dereferenced so the calls go through the vtable.
9 changes: 9 additions & 0 deletions hints/06_raii/raii1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

RAII means a resource acquired in the constructor is released in the
destructor. `Holder` allocates an `ExpensiveResource` with `new`, so its
destructor must `delete` it, otherwise the count never returns to zero
(a leak, CWE-401).

The destructor is declared but has no name or body. Give it the class name
(`~Holder`) and release the resource inside it.
9 changes: 9 additions & 0 deletions hints/06_raii/raii2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

Here the resource count is tracked by an `std::unordered_set` keyed on the
resource name. The constructor should register the resource and the
destructor should remove it.

Look at the two `resources. ...;` lines: the constructor needs `insert`
(add the name) and the destructor needs `erase` (remove it). Because a set
holds each key only once, opening the same name twice does not grow the count.
8 changes: 8 additions & 0 deletions hints/07_containers/containers1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

A raw C array `int my_sequence[num_elements]` has no `.size()` member and no
`.begin()/.end()`, which is why the test does not compile.

Replace it with the STL container that is a fixed-size array but knows its own
size: `std::array<int, num_elements>`. The rest of the code (indexing and the
range-based for loop) then works unchanged.
7 changes: 7 additions & 0 deletions hints/07_containers/containers2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Hint

The test inserts the same value `42` several times but expects the final size
to be `1`: duplicates must collapse into a single element.

That is the job of a set. Look at `std::unordered_set<int>` (or `std::set<int>`)
and use its `insert` member instead of index assignment.
8 changes: 8 additions & 0 deletions hints/07_containers/containers3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

The code assigns at sparse, non-contiguous indices (0, 50, 100, 150, 200) and
then iterates only over the elements that were actually set. A plain array or
`std::vector` would need every slot up to 200.

A key-to-value associative container fits: look at `std::map<int, int>` (or
`std::unordered_map`). Its header is what belongs in the `#include <?>` line.
9 changes: 9 additions & 0 deletions hints/08_ownership/ownership1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`fill_vec` takes its vector **by value**, so it modifies a copy and the caller's
`vec` never sees the three `push_back`s. The test expects the original to grow
to 5 elements.

Following the ownership conventions in the chapter README, borrowing a variable
to modify it in place is done with a reference: change the parameter type so
`fill_vec` operates on the caller's vector directly.
9 changes: 9 additions & 0 deletions hints/08_ownership/ownership2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

The goal is to *transfer* ownership of the data out of `vec0` into `vec1`,
leaving `vec0` empty (the test requires `vec0.size() == 0`).

`fill_vec` takes an rvalue reference (`std::vector<int>&&`), so it wants
something it is allowed to steal from. Look up `std::move`: it casts an lvalue
to an rvalue so its contents can be moved rather than copied. The two `...?`
holes each need the value to be moved.
8 changes: 8 additions & 0 deletions hints/08_ownership/ownership3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

`holder_list` holds `std::unique_ptr<Holder>` (a `HolderBox`), and a
`unique_ptr` cannot be copied, only moved. `push_data` receives `hold_ptr` by
value, so it already owns a `unique_ptr` it must hand into the vector.

Look up `std::move`: `push_back` needs the argument moved in, not copied.
Changing only that one line makes it compile.
9 changes: 9 additions & 0 deletions hints/08_ownership/ownership4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`HolderBox` is a raw `Holder*`, and `&h` takes the address of a local that is
destroyed at the end of each loop iteration, so the vector ends up full of
dangling pointers (CWE-416: Use After Free).

Redefine `HolderBox` as a `std::unique_ptr<Holder>` so the vector owns each
`Holder`, and create it with `std::make_unique<Holder>(s)` instead of `&h`.
The owning pointer keeps the resource alive and releases it exactly once.
8 changes: 8 additions & 0 deletions hints/08_ownership/ownership5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

`fill_vec` uses `vec->push_back(...)`, so it expects a pointer, not a value.
Change its parameter type to a raw pointer `std::vector<int>*` so it borrows
the vector without owning it (it must not delete it).

`vec` is a `std::unique_ptr`. Look up the member that hands out the raw
underlying pointer without giving up ownership, and pass that to `fill_vec`.
10 changes: 10 additions & 0 deletions hints/08_ownership/ownership6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Hint

`std::shared_ptr` lets several owners share the same data; `use_count()`
reports how many owners currently exist. The exercise wants an extra
"artificial" owner so the count rises.

The line marked TODO already constructs a second `shared_ptr` from the
argument. Let the compiler deduce the type instead of spelling it out: replace
the explicit datatype so the copy still shares ownership of the same block.
Copying a `shared_ptr` (not moving it) is what bumps `use_count()`.
8 changes: 8 additions & 0 deletions hints/09_templates/templates1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

`max` currently only accepts `int`. To let the same function work for both
`int` and `double`, turn it into a function template.

Put `template<typename T>` in front of the function and replace the concrete
`int` types (parameters and return) with `T`. The compiler then stamps out one
version per type you call it with.
8 changes: 8 additions & 0 deletions hints/09_templates/templates2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hint

`max` only takes a `std::vector<int>`. Make it a template so it also accepts a
`std::vector<double>`.

Template the element type: `template<typename T>` and take a
`const std::vector<T>&`. The local maximum and the return type should be `T`
too, so nothing is truncated back to `int`.
9 changes: 9 additions & 0 deletions hints/09_templates/templates3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

Now the container itself varies: a `std::vector<int>` and a
`std::deque<double>`. One template type parameter for the element is not
enough.

Template on the whole container type instead, e.g. `template<typename Container>`
taking `const Container&`. Return `auto` so the element type is deduced from
whatever the container holds.
10 changes: 10 additions & 0 deletions hints/09_templates/templates4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Hint

The `?` placeholders are the template parameter. Replace `typename ?` with a
real type parameter name (e.g. `typename T`) on both `Animal` and `Cat`, and use
that name where the `?` and `int` members appear.

`Cat` derives from `Animal`, so it must forward its type argument to the base:
`Cat : public Animal<T>` and `Animal<T>(years)` in the constructor. Class
template argument deduction then lets `Cat blue(4)` deduce the type from the
constructor argument.
15 changes: 15 additions & 0 deletions hints/09_templates/templates6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Hint

`is_string` is already written for you: a primary template inheriting from
`false_type`, plus a full specialization for `std::string` inheriting from
`true_type`. Follow the exact same pattern to define `is_vector`.

The difference is that a vector is itself a template, so the "true" case is a
partial specialization that keeps the element type generic:

```cpp
template<typename T> struct is_vector : false_type {};
```

Add the partial specialization that matches `std::vector<T>` for any `T`.
`is_pointer` in the next exercise follows the same idea for `T*`.
9 changes: 9 additions & 0 deletions hints/09_templates/templates7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Hint

`is_pointer<T>` from the previous exercise gives a compile-time `::value` that
is true when `T` is a pointer. `if constexpr` picks a branch at compile time
based on that value, so the discarded branch does not even need to be valid for
the other type.

Put the trait check inside the `if constexpr ( ... )` condition. Remember the
type of the argument is the template parameter `C`.