Search

9.10 — Overloading typecasts

In the lesson 4.4 -- Type conversion and casting, you learned that C++ allows you to convert one data type to another. The following example shows an int being converted into a double:

C++ already knows how to convert between the built-in data types. However, it does not know how to convert any of our user-defined classes. That’s where overloading the typecast operators comes into play.

Overloading the typecast operators allows us to convert our class into another data type. Take a look at the following class:

This class is pretty simple: it holds some number of cents as an integer, and provides access functions to get and set the number of cents. It also provides a constructor for converting an int into a Cents.

If we can convert an int into a Cents, then doesn’t it also make sense for us to be able to convert a Cents back into an int? In some cases, this might not be true, but in this case, it does make sense.

In the following example, we have to use getCents() to convert our Cents variable back into an integer so we can print it using printInt():

If we have already written a lot of functions that take integers as parameters, our code will be littered with calls to getCents(), which makes it more messy than it needs to be.

To make things easier, we can overload the int typecast, which will allow us to cast our Cents class directly into an int. The following example shows how this is done:

There are two things to note:

  1. To overload the function that casts our class to an int, we write a new function in our class called operator int(). Note that there is a space between the word operator and the type we are casting to.
  2. Casting operators do not have a return type. C++ assumes you will be returning the correct type.

Now in our example, we can call printInt() like this:

The compiler will first note that function printInt takes an integer parameter. Then it will note that variable cents is not an int. Finally, it will look to see if we’ve provided a way to convert a Cents into an int. Since we have, it will call our operator int() function, which returns an int, and the returned int will be passed to printInt().

We can now also explicitly cast our Cents variable to an int:

You can overload cast operators for any data type you wish, including your own user-defined data types!

Here’s a new class called Dollars that provides an overloaded Cents cast operator:

This allows us to convert a Dollars object directly into a Cents object! This allows you to do something like this:

Consequently, this program will print the value:

900

which makes sense, since 9 dollars is 900 cents!

9.11 -- The copy constructor
Index
9.9 -- Overloading the parenthesis operator

13 comments to 9.10 — Overloading typecasts

  • Zafer

    To convert a Dollar object to a Cents object, Dollar class uses Cents class by returning Cents(m_nDollars * 100). Shouldn’t we declare the Cents class before Dollar to be able to generate a Cents object?

  • Brooklyn

  • Steiner

    Hello Alex,

    I tried to expand your code and it gave me errors that I don’t even understand on how to solve it using the lessons learned on this chapter.

    this gives out a compiler error of:
    #1. "error: expected type-specifier before ‘Meters’
             operator Meters() { return Meters(m_unit * 1000); }"

    and

    #2. "error: could not convert ‘value2’ from ‘Millimeters’ to ‘Meters’
             PrintMeters(value2);"

    I thought of solving error #1 would be by doing a forward declaration of "class Meters" (as shown in the commented code).
    After doing that, the compiler showed a new kind of error:
    "error: return type ‘class Meters’ is incomplete
             operator Meters() { return Meters(m_unit * 1000); }"

    which doesn’t seem to mean anything. It might be the compiler doesn’t have a complete idea about the "class Meters" for it to be a return type?

    The #2 error is very odd since the call "PrintMillimeters(value1);" worked (didn’t give any compiler errors) which is expected to call in the operator() to do a Meters to Millimeters conversion.

    But the other way around, the call "PrintMeters(value2);" gave a compiler error as written above in #2.

    Thank you again for this tutorials.

    • Alex

      The problem is that you have a circular dependency here. Meters depends on knowing the declaration of Millimeters, and Millimeters depends on knowing the declaration of Meters.

      The best solution is to move each class into its own header file, and move the function implementations into a .cpp file.

      That way each class can see the declaration of the other, and the classes will be fully declared before the function definitions are compiled.

      • Steiner

        Hello Alex,

        Thank you for your time.

        Unfortunately, I’m still having the same compilation problems even after I did your proposed solution and checking online for other solutions :(. I made sure I included the Header Guards, placed the function implementations into their respective .cpp files, and use the #include directive to include the necessary header files in each of the .cpp file. Here’s the complete separated code:

        Meters.h

        Meters.cpp

        Millimeters.h

        Millimeters.cpp

        main.cpp

        The compilation problem (codeblocks, g++ 4.9.2) still says:
        1. Meters.h|18|error: expected type-specifier before ‘Millimeters’
        2. main.cpp|33|error: could not convert ‘value1’ from ‘Meters’ to ‘Millimeters’

        Just answer this question whenever you can and sorry for taking your time. I’m on 10.4 container classes now and I’m really learning a lot. I’m also making sure to read most if not all the discussions found at the end of the chapters :). So, Thank you for the lessons!

        • Alex

          The problem is that Millimeters.h is including Meters.h, which includes Millimeters.h -- essentially a circular reference. Due to the header guards on Millimeters.h, when Meters.h includes Millimeters.h, the header file doesn’t get included, so Meters.h doesn’t see the declaration for Millimeters.h.

          Fortunately, in this case there’s an easy fix. Instead of having each header #include the other, replace the #includes with forward declarations for the other class. For example, at the top of Millimeters.h, replace #include “Meters.h” with “class Meters;” And do vice-versa for Meters.h.

          That way, the overloaded typecast in the header will know that Meters and Millimeters are types, so it will compile.

          Make sense?

          • Steiner

            I think I get it now. I thought that this is the sequence that the compiler does:

            processing the file Millimeters.h (goes through Millimeters.h to Meters.h then back to Millimeters.h)
            -MILLIMETERS_H gets defined
            -include "Meters.h"
            -goes inside Meters.h file
            -sees #include "Millimeters.h"
            -goes inside Millimeters.h
            -ignores this file since MILLIMETERS_H was already defined and skips this header file (due to #ifndef)
            -goes back to Meters.h file
            -proceeds on the class definition of class Meters

            Which prevents circular reference.

            However I forgot to add in how the compiler processes the other file as well.

            Now when processing the file Meters.h (goes through Meters.h to Millimeters.h then back to Meters.h) and as you said, due to the header guards on Millimeters.h
            -METERS_H was already defined so this file doesn’t get processed
            -so Meters.h doesn’t see the declaration for Millimeters.h

            Thank you Alex, I did the replacement and made the program run. I learned a lot from you :D.

  • Ola Sh

    Hi Alex,

    I would like to know if you can overload a typecast operator to convert a Cents object to a std::string object. I tried to overload the operator, but Visual Studio points out that no operator exists for converting int to strings. Can I define or overload an operator to convert from integers to strings? Thanks for your help.

    • Alex

      You can definitely overload a typecast to convert your class to a string. The challenge you’re running into here isn’t that, but rather figuring out how to convert an integer to a std::string in the first place. Prior to C++11, this isn’t easy. But in C++11, you can use the std::to_string() function:

      That said, if your goal is just to print the state of something, you’re better off overloading operator>>.

  • Ola Sh

    Thanks for your help.

  • Hunter

    Hi Alex:
    In the last example above,why can’t

    pass value by reference like this?

    • Alex

      The implicit conversion from Dollars to Cents creates a temporary (anonymous) Cents object. In the case where the function parameter is passed by value, this anonymous Cents object is copied into the Cents parameter. However, non-const references aren’t allowed to be assigned to anonymous objects, so in the case where the function parameter is a reference, the conversion can’t happen.

      Since printCents doesn’t modify the parameter, a better thing to do would be to make printCents take a const reference (which can be bound to a temporary object). However, our conversion to int() won’t work in this case, because it’s not flagged as const, so we’d need to modify the overloaded int cast to be const (which we probably should have done anyway).

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter

  

  

  

10 + thirteen =