Skip to content

← Const Correctness and Class Design step 2 of 3

Medium Primitives

The conversion nobody asked for

class Fahrenheit {
public:
    Fahrenheit(double degrees);
};

That is not only a constructor. It is a standing instruction to the compiler: any double may become a Fahrenheit, silently, wherever one is wanted. Returning one, passing one, comparing one — the conversion is applied without a cast, a warning, or a word in the source.

Which means a class written to make units unmixable does not:

Fahrenheit convert(Celsius c) {
    return c.degrees();     // compiles. It is not a conversion, it is a relabel.
}

The whole point of having two types was to make that line impossible, and one missing keyword handed the compiler permission to write it for you.

explicit

explicit Fahrenheit(double degrees);

Now the type must be named at every construction: Fahrenheit(x) or Fahrenheit{x}. The line above stops compiling, and the error appears at the place where the mistake actually is.

Single-argument constructors are explicit by default, as a habit. Drop it only when the conversion is genuinely something callers should be able to write without thinking — which in practice means when the two types are the same idea in two representations, like std::string from a string literal.

Note that “single-argument” includes constructors where the rest have defaults, and that since C++11 explicit matters for multi-argument constructors too, because a braced list can be implicitly converted.

The other half: conversion operators

operator bool() const;             // every Handle is now also an int, a
                                   // char, a comparison operand...
explicit operator bool() const;    // usable in `if (h)`, and nowhere else

An implicit operator bool is the classic version of this bug, because bool promotes to int and from there everything is arithmetic. explicit operator bool is special-cased by the language to still work in conditions, so you lose nothing.

The gate checks this

google-explicit-constructor is enabled, so an implicit single-argument constructor is a gate failure here, not just advice.

Your task

Fahrenheit to_fahrenheit(Celsius c);

Convert. F = C × 9/5 + 32.

Celsius is given and already correct — look at how it is declared. Fix Fahrenheit to match, and then fix to_fahrenheit, which currently returns a Celsius number wearing a Fahrenheit label and is only able to because of the missing keyword.

report is given: it converts each reading and flags the ones above a threshold. Do not change it.