Operator Overloading

Conversion Operators

A conversion operator allows your class objects to be implicitly converted to other types. They are defined like normal member functions, but are named: operator followed by the type that your object should be converted to. If the compiler requires a value of the type in question and you have a value of your object type, then it will automatically call the conversion operator.

You can start by defining a simple operator for converting to built in types. Below we have a small class for storing one value which you can convert to a bool.

#include <iostream>

class Flag
{
private:
    int m_value{};

public:
    explicit Flag(int v)
        : m_value{ v }
    {
    }

    // Define how this operator works for the class
    operator bool() const
    {
        return m_value != 0;
    }
};

int main()
{
    Flag f{ 5 };

    if (f)
        std::cout << "flag is set\n";

    Flag g{ 0 };

    if (!g)
        std::cout << "flag is clear\n";

    return 0;
}

The operator bool() simply checks if m_value is nonzero or not. As it has not been marked as explicit, the compiler will implicitly call the operator. This is why if (f) will work, as it needs a bool for the if condition, when it sees the Flag it will convert.

This can be used for any other type: a class that stores a value in Centimetres could have an operator for converting it into metres, stored as a double.

#include <iostream>

// Define the members that belong to this type
class Centimetres
{
private:
    double m_cm{};

public:
    explicit Centimetres(double cm)
        : m_cm{ cm }
    {
    }

    operator double() const
    {
        return m_cm / 100.0;
    }
};

int main()
{
    Centimetres height{ 185.0 };
    double metres{ height };

    std::cout << metres << '\n';
    return 0;
}

As before, you would like to set the variable metres equal to height. When the compiler would like to define metres this way, it will look up what converts to double and see operator double() and divide the value stored by 100 to return. The compiler again does not need to be told what to do by the programmer because the operator has not been marked as explicit.

Whereas implicit conversion is quite easy, it is rarely obvious when one may occur. This is why C++ allows you to have an explicit conversion operator.

explicit operator double() const
{
    return m_cm / 100.0;
}

If you were to run this new code, it would fail.

Centimetres height{ 185.0 };
double metres{ height };

This is because if height is of Centimetres, the C++ compiler will try and set double metres. As there is no implicit conversion operator for Centimetres to a double set, as we marked it as explicit, the code fails. So it can be set by explicitly casting.

// Perform this conversion explicitly
double metres{ static_cast<double>(height) };

The conversion is taking place, but the programmer has had to tell the C++ compiler.

A conversion operator never has parameters, never uses a traditional return type. The type that your object is converted to is named straight after operator. These sorts of operators are always placed in the members list as const, as there is never a reason for an object to change when converting itself. If the conversion operator is in there anywhere that requires the target type, that object can implicitly be placed in there, or explicitly be cast in there.

Exercise: Conversion OperatorsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Conversion Operators

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Spell the first condition using an explicit call to f.operator bool(). Predict both flag messages and explain the stored-value test.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

class Flag
{
private:
    int m_value{};

public:
    explicit Flag(int v)
        : m_value{ v }
    {
    }

    // Define how this operator works for the class
    operator bool() const
    {
        return m_value != 0;
    }
};

int main()
{
    Flag f{ 5 };

    if (f)
        std::cout << "flag is set\n";

    Flag g{ 0 };

    if (!g)
        std::cout << "flag is clear\n";

    return 0;
}

Hints

Hint 1

if (f) asks Flag to convert itself to bool. Read operator bool(): it tests m_value != 0 rather than exposing the integer directly.

Hint 2

Compare Flag{5} and Flag{0}, then inspect the ! applied to g. You can spell the conversion explicitly as f.operator bool() to see the function being used.

Worked solution

The first Boolean conversion is explicitly written as f.operator bool(). It returns true for the stored 5, so the set message still appears. g stores zero and its negated conversion still selects the clear message.

#include <iostream>

class Flag
{
private:
    int m_value{};

public:
    explicit Flag(int v)
        : m_value{ v }
    {
    }

    // Define how this operator works for the class
    operator bool() const
    {
        return m_value != 0;
    }
};

int main()
{
    Flag f{ 5 };

    if (f.operator bool())
        std::cout << "flag is set\n";

    Flag g{ 0 };

    if (!g)
        std::cout << "flag is clear\n";

    return 0;
}

Example output

flag is set
flag is clear

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.