C++ is, in principle, a strongly typed language. It does, however, allow some implicit conversions between types, making it “not quite as strong as it could be”-typed. Historically, a lot of these implicit conversions come from C — a language which we all agree could use a little less implicit conversions. C++ also doesn’t have a really easy way to add “units” to types, and allows any typedef to be implicitly converted to another.


Before we get to a solution to the issue, we need to understand what the issue is, and why it’s an issue.

To understand what the issue is, consider this example of a “pizza-slice” area calculation, where we want to calculate the area of a segment of a circle:

image of a circle

In this example we will use a circle of radius r = 5.0 cm and a segment with an angle of 50.86°. The area of the segment then works out to be around 11.1 cm².

The code for this might look something like this:

float area_of_circle_sector(float radius, float angle) {
    return (angle / 360.0f) * M_PI * radius * radius;
}

Now, this code works, but it has a lot of pitfalls. Is angle in degrees or radians? Is radius in meters, centimeters, feet, …? What if we called it with radius and angle swapped, by accident (since they are both float)?

float radius = 5.0f;
float angle = 50.86f;
// arguments are swapped
float result = area_of_circle_sector(angle, radius);
// converting angle from degrees to radians, even though this is wrong
float result = area_of_circle_sector(radius, angle * 0.0175f);

Now that we have understood the issue, let’s address it.

A common first step would be to add documentation and extend the naming to angle_in_degrees, maybe even add some typedef’s and using’s to alias float to different names. And while these are all great, let’s consider using structs to encapsulate our types in the type-system, and thus offloading this work to the compiler:

struct Degrees {
    float value;
};

struct Length {
    float value;
};

struct Area {
    float value;
};

Area area_of_circle_sector(Length radius, Degrees angle) {
    return { (angle.value / 360.0f) * M_PI * radius.value * radius.value };
}

This adds a lot of code, but not much complexity, and any optimizing compiler will optimize the structs away. But now, when you try passing a length to an angle, you get a compile-time error! The compiler now ensures that you can’t pass an angle to a length argument — and that is very, very powerful.

Now, there are a few issues with this:

  1. You can define all sorts of fancy operators (operator+, operator*), and end up with a massive amount of complexity. I have deliberately not done this, since it defeats the purpose of the “simplicity” of this solution, and is in no way necessary.

  2. Your types are still not perfect. This example doesn’t “check” if you’re passing centimeters to feet, or anything like that, but that can easily be achieved by pushing this concept further with LengthInCm and similar.

You can, however, further expand this to have helpers, for example the Degrees type should probably have a to_radians method, or maybe even an implicit conversion via operator Radians() , and of course assignment operator(s), which I left out for clarity’s sake.