Chapter 13: Operator Overloading

Introduction

Operator overloading allows you to redefine the way operators work with user-defined types like classes and enums. This chapter covers how to overload operators for custom types, with examples including enums and I/O stream operations.

Example with Enums

Enum Definition

#include <iostream>
#include <string_view>
#include <optional>

enum Color
{
    black,
    red,
    blue,
};

constexpr std::string_view getColorName(Color color)
{
    switch (color)
    {
    case black: return "black";
    case red:   return "red";
    case blue:  return "blue";
    default:    return "???";
    }
}

enum Pet
{
    cat,
    dog,
    pig,
    whale,
};

constexpr std::optional<Pet> getPetFromString(std::string_view sv)
{
    if (sv == "cat")   return cat;
    if (sv == "dog")   return dog;
    if (sv == "pig")   return pig;
    if (sv == "whale") return whale;

    return {};
}

Overloading operator<< for Output

To overload the << operator for output, we define a function that takes an std::ostream reference and an enum value.

// Teach operator<< how to print a Color
std::ostream& operator<<(std::ostream& out, Color color)
{
    out << getColorName(color); // print our color's name to whatever output stream out
    return out;                 // operator<< conventionally returns its left operand
}

Overloading operator>> for Input

To overload the >> operator for input, we define a function that takes an std::istream reference and an enum reference.

// pet is an in/out parameter
std::istream& operator>>(std::istream& in, Pet& pet)
{
    std::string s{};
    in >> s; // get input string from user

    std::optional<Pet> match { getPetFromString(s) };
    if (match) // if we found a match
    {
        pet = *match; // set Pet to the matching enumerator
        return in;
    }

    // We didn't find a match, so input must have been invalid
    // so we will set input stream to fail state
    in.setstate(std::ios_base::failbit);

    // On an extraction failure, operator>> zero-initializes fundamental types
    // Uncomment the following line to make this operator do the same thing
    // pet = {};

    return in;
}

Main Function

The main function demonstrates how to use the overloaded operators.

int main()
{
    Color shirt{ blue };
    std::cout << "Your shirt is " << shirt << '\n'; // it works!

    std::cout << "Enter a pet: cat, dog, pig, or whale: ";
    Pet pet{};
    std::cin >> pet;

    if (std::cin) // if we found a match
        std::cout << "You chose: " << getPetName(pet) << '\n';
    else
    {
        std::cin.clear(); // reset the input stream to good
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Your pet was not valid\n";
    }

    return 0;
}