Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Custom Type Conversion Operators
Define implicit and explicit conversions to other types.
Overloading typecasts
Just as C++ can convert between built-in types like int and double, we can define how our custom classes convert to other types using typecast operator overloading.
The problem: converting class types
Consider this Percentage class:
#include <iostream>
class Percentage
{
private:
double m_value{};
public:
explicit Percentage(double value) : m_value{value} {}
double getValue() const { return m_value; }
};
void displayProgress(double progress)
{
std::cout << progress << "%\n";
}
int main()
{
Percentage completion{75.5};
displayProgress(completion.getValue()); // Awkward!
return 0;
}
Having to call getValue() every time we want to use our Percentage as a double is cumbersome. We can solve this with a typecast operator.
Overloading a typecast operator
Here's how to overload a typecast to double:
#include <iostream>
class Percentage
{
private:
double m_value{};
public:
explicit Percentage(double value) : m_value{value} {}
operator double() const { return m_value; }
double getValue() const { return m_value; }
};
void displayProgress(double progress)
{
std::cout << progress << "%\n";
}
int main()
{
Percentage completion{75.5};
displayProgress(completion); // Implicit conversion to double!
std::cout << static_cast<double>(completion) << '\n'; // Explicit cast also works
return 0;
}
Key points about typecast operators:
- They're written as
operator TypeName() const - They must be non-static member functions
- They should generally be
const(they don't modify the object) - They don't declare a return type (it's implicit in the operator name)
- They have no parameters (aside from the hidden
*this)
Converting between custom types
You can convert between custom types too. Here's a Radians class with conversion to Degrees:
#include <iostream>
class Degrees
{
private:
double m_degrees{};
public:
explicit Degrees(double deg) : m_degrees{deg} {}
operator double() const { return m_degrees; }
};
class Radians
{
private:
double m_radians{};
static constexpr double PI{3.14159265358979};
public:
explicit Radians(double rad) : m_radians{rad} {}
operator Degrees() const
{
return Degrees{m_radians * 180.0 / PI};
}
};
void displayAngle(Degrees angle)
{
std::cout << static_cast<double>(angle) << " degrees\n";
}
int main()
{
Radians halfCircle{3.14159265358979};
displayAngle(halfCircle); // Converts Radians -> Degrees
return 0;
}
Output:
180 degrees
Explicit typecasts
Just like constructors, typecasts can be marked explicit to prevent implicit conversions:
#include <iostream>
class Percentage
{
private:
double m_value{};
public:
explicit Percentage(double value) : m_value{value} {}
explicit operator double() const { return m_value; }
};
void displayProgress(double progress)
{
std::cout << progress << "%\n";
}
int main()
{
Percentage completion{75.5};
// displayProgress(completion); // Error: no implicit conversion
displayProgress(static_cast<double>(completion)); // OK: explicit cast
return 0;
}
Mark typecast operators as `explicit` unless the conversion is cheap and the types are conceptually equivalent.
Converting constructor vs typecast operator
There are two ways to enable conversion between types:
- Converting constructor: A constructor in class B that takes class A as a parameter
- Typecast operator: A typecast operator in class A that converts to class B
Which should you use?
// Option 1: Converting constructor in Degrees
class Degrees
{
public:
Degrees(const Radians& rad); // Degrees knows about Radians
};
// Option 2: Typecast operator in Radians
class Radians
{
public:
operator Degrees() const; // Radians knows about Degrees
};
Prefer converting constructors over typecast operators. A class should own its own construction logic.
Use typecast operators when:
- Converting to a fundamental type (you can't add constructors to
intordouble) - Converting to a type you can't modify (like standard library types)
- You want to avoid circular dependencies
- The typecast returns a reference
A complete example
Here's a practical Duration class with conversions:
#include <iostream>
class Duration
{
private:
int m_seconds{};
public:
explicit Duration(int seconds) : m_seconds{seconds} {}
explicit operator int() const { return m_seconds; }
explicit operator double() const { return static_cast<double>(m_seconds); }
int getSeconds() const { return m_seconds; }
int getMinutes() const { return m_seconds / 60; }
int getHours() const { return m_seconds / 3600; }
};
int main()
{
Duration movie{7200}; // 2 hours
std::cout << "Duration: " << static_cast<int>(movie) << " seconds\n";
std::cout << "That's " << movie.getHours() << " hours\n";
return 0;
}
Summary
Typecast operator overloading: Defines how custom classes convert to other types, written as operator TypeName() const.
Syntax requirements: Must be non-static member functions, generally should be const, have implicit return type (specified in operator name), and take no parameters aside from *this.
Implicit vs explicit conversions: By default, typecast operators allow implicit conversions. Mark as explicit to require explicit casts using static_cast.
Converting between custom types: Typecast operators work for conversions to other custom classes, not just fundamental types.
Converting constructor vs typecast operator: Two ways to enable conversion between types - a constructor in the target class (converting constructor) or a typecast operator in the source class. Prefer converting constructors when possible as classes should own their construction logic.
When to use typecast operators: Use when converting to fundamental types (can't add constructors to int), converting to types you can't modify (standard library classes), avoiding circular dependencies, or when the cast returns a reference.
Best practice: Mark typecast operators as explicit unless the conversion is cheap and the types are conceptually equivalent, preventing surprising implicit conversions.
Typecast operators eliminate awkward getValue() calls when working with custom classes, but should be used judiciously to avoid implicit conversions that reduce code clarity.
Custom Type Conversion Operators - Quiz
Test your understanding of the lesson.
Practice Exercises
Percentage Class with Typecast Operators
Create a Percentage class that stores values from 0-100. Implement typecast operators to convert to double (as decimal 0.0-1.0) and to int (as whole percentage). Practice both explicit and implicit conversion scenarios.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!