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:
1 2 |
int n{ 5 }; auto d{ static_cast<double>(n) }; // int cast to 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.
User-defined conversions allow us to convert our class into another data type. Take a look at the following class:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Cents { private: int m_cents; public: Cents(int cents=0) : m_cents{ cents } { } int getCents() const { return m_cents; } void setCents(int cents) { m_cents = cents; } }; |
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():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void printInt(int value) { std::cout << value; } int main() { Cents cents{ 7 }; printInt(cents.getCents()); // print 7 std::cout << '\n'; return 0; } |
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 provide a user-defined conversion by overloading the int typecast. This will allow us to cast our Cents class directly into an int. The following example shows how this is done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Cents { private: int m_cents; public: Cents(int cents=0) : m_cents{ cents } { } // Overloaded int cast operator int() const { return m_cents; } int getCents() const { return m_cents; } void setCents(int cents) { m_cents = cents; } }; |
There are three things to note:
- 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.
- User-defined conversions do not take parameters, as there is no way to pass arguments to them.
- User-defined conversions 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:
1 2 3 4 5 6 7 8 9 |
int main() { Cents cents{ 7 }; printInt(cents); // print 7 std::cout << '\n'; return 0; } |
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:
1 2 |
Cents cents{ 7 }; int c{ static_cast<int>(cents) }; |
You can provide user-defined conversions 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 conversion:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Dollars { private: int m_dollars; public: Dollars(int dollars=0) : m_dollars{ dollars } { } // Allow us to convert Dollars into Cents operator Cents() const { return Cents{ m_dollars * 100 }; } }; |
This allows us to convert a Dollars object directly into a Cents object! This allows you to do something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <iostream> class Cents { private: int m_cents; public: Cents(int cents=0) : m_cents{ cents } { } // Overloaded int cast operator int() const { return m_cents; } int getCents() const { return m_cents; } void setCents(int cents) { m_cents = cents; } }; class Dollars { private: int m_dollars; public: Dollars(int dollars=0) : m_dollars{ dollars } { } // Allow us to convert Dollars into Cents operator Cents() const { return Cents(m_dollars * 100); } }; void printCents(Cents cents) { std::cout << cents; // cents will be implicitly cast to an int here } int main() { Dollars dollars{ 9 }; printCents(dollars); // dollars will be implicitly cast to a Cents here std::cout << '\n'; return 0; } |
Consequently, this program will print the value:
900
which makes sense, since 9 dollars is 900 cents!
![]() |
![]() |
![]() |
Hi there! You wrote that "There are two things to note "after the fourth snippet but it should be "there are three things to note". Sorry to disturb you but advanced thanks.
Thanks!
Hi.Are there rules in selecting the implicit operator overload by compiler? I mean the first example we had
and it is obvious it should cast to int (and int is printable by <<) but in the following:
Why this implicit will occur at all? Is it because the only thing printable by << is the int() overload?
I change the int() overload on Cents to short() and it compiled but I assume it overflowed on
prints : 24464
I create a << overload in addition to short() and it goes right.
Changed back short() to int() to see the result.
I see (through step into), in presence of int() overload and << overload , the << overload called, even though the int() has been defined before << overload.
prints: 90000
> Hi.Are there rules in selecting the implicit operator overload by compiler?
Yes https://en.cppreference.com/w/cpp/language/implicit_conversion
> Why this implicit will occur at all? Is it because the only thing printable by << is the int() overload? Yes > I create a << overload in addition to short() Now you're giving the compiler a way of printing your object without any conversions. That's better than having to convert the object, so the compiler uses your operator. The order of definition is irrelevant.
>We can now also explicitly cast our Cents variable to an int:
Maybe there's no need...I think this should work:
The point of this example is to show that `Cents` can be _explicitly_ cast. Your suggestion removes the explicit cast.
I tested this
and it prints "A\n2". Does this mean that we provide casts of other types to our type by implementing a constructor that takes the other type as an input?
nvm I arrived at 9.13 =)
Why are we unable to write it like this?
That will only work with non-const lvalues.
If you don't modify a reference, make it const. const references can bind to all kinds of values.
I wanted to overload the '->' operator. Does it have to return a pointer type? What e is happening here?
Why doesn't that work? What did I misunderstand here? I think it works like this. a->some_T2_method() should be evaluated as obj.some_T2_method(). I ... am also thinking that -> does nothing but put whatever is the result of its overload behind it and call its -> operator, hence, I need to return a pointer. So, it should actually be
which leads me to think the second one is correct; and in fact, that is what works! So I guess my second thinking is what actually happens?
Your assumption is correct. `operator->` doesn't _have to_ return a pointer. It has to either return a pointer, or return an object that has an `operator->` that returns a pointer (Or returns an object with `operator->` that returns an object with `operator->` that ...).
You first example is valid if `T2` has a valid `operator->`.
Ahhh yes! It's all making sense! So it recursively calls the operator till method/property is evaluated finally! Damn took me a while to understand it hahah.
I can't seem to recall this being discussed here?
Also, took me a while to resolve this issue. This is KEY to remember (8.5A -> Initializer list order)!
Thank you very much for your help!
P.S. How do I get a grasp on the more advanced topics of C++? Could you kindly address this in depth at the end of all the lessons?
> I can't seem to recall this being discussed here?
I don't think it is. Overloading `operator->` is rare.
> Obj::Obj (... parameters)
Careful there, `...` has special meanings in C++, you confused me for a moment. `/* ... */` is better.
> failed to copy over the string properly
That's odd. Class-type members are default-initialized. In `Obj::Obj`'s body, every string should have been initialized already. The constructor body is the last thing that gets executed during initialization. Default-initialized members and member initializer lists have been processed before that.
I don't want to be in a situation like the one in the blog entry. But it goes to show how deep one might have to get into the system as a C++ programmer.
> How do I get a grasp on the more advanced topics of C++?
What you're doing right now is the best thing I can recommend. You're working with advanced code, you'll inevitably find things you don't understand, then you learn more about them. Also, conference talks (eg. cppcon) are great to get information from someone who spent a lot of time on a topic (You're watching those iirc). It's also good to stay in touch with standard updates. You'll know about features before you can use them. https://en.cppreference.com/w/cpp/compiler_support . Also go through the tables of the older standards on that page, you'll find many new things.
> Careful there, `...` has special meanings in C++, you confused me for a moment. `/* ... */` is better.
Hahah yes, my bad!
> That's odd
Yeah, I was thinking that too! I dug up my undo history, and I think this reflects the case more closely. Could you edit the comment with this if possible? Thanks!
Yeah that was spaghetti code. I made classes for no reason. Instead, I made them into regular functions and grouped related ones in namespaces. Now, it's much cleaner!
Glad to know I'm on the right track! Thank you for the kind words of motivation!
Please fix:
code above forgot the semi colon below the constructors Dollars(int dollars = 0) and Cents(int dollars = 0)
Missing colons added, thanks!
Colon seems to be missing in some constructor functions in the lesson -
Lesson updated, thanks for pointing out the error!
Perhaps it's worth mentioning that
causes an implicit cast of int (the 5) to Cents via the constructor we have defined for Cents. (realized this thanks to Nascardriver's help with my doubt on Fraction exercise/example a few lessons back!)
Also, the last thing the last code snippet prints is not a newline :) (I enabled by -pedantic flag)
> Perhaps it's worth mentioning
Lesson 9.13 is dedicated to this conversion.
> the last thing the last code snippet prints is not a newline
Lesson updated, thanks!
Onwards to 9.13!
My pleasure!
Shouldn't the operator functions always be declared const?
I came across that in the comprehensive quiz, when trying to convert the FixedPoint2 class to a double inside the operator<< function body.
Yep. Lesson updated. Thanks!
Hi, Alex.
Then, dollars's data type is changed to Cents forever?
Or dollars is temporarily denoted Cents and remained Dollars data type?
Types are never changed.
In the case where Dollars::operator Cents() is executed, a new Cents object is returned. The original Dollars object is not altered.
Hi Alex,
Good to add another point stating that "These convertion operators cannot take any parameters in the function definition".
Done. Thanks for the suggestion.
When debugging this, I noticed "operator int() { return m_penny; }" (in class Penny) is required for the program to work when compiling "printPenny(pound, pound);" Why is the operator int() needed for Penny in this case when I only use the class Pounds for use? ( other than calling for it in "operator Penny() { return Penny(m_pounds * 100); }" )
Line 33 prints an instance of @Penny.
@std::cout::operator<< doesn't know how the print a @Penny, but it knows how to print an int, so it casts.
Cents class has an overloaded int() typecast so int(cents) works. But how does the compiler know we want to implicitly cast cents to an int and not some other type? Does the compiler check for overloaded typecast in class definition to see which type to implicitly cast to?
Thanks
Operator<< has functions that work with all kinds of different types. First it will try and find a match for Cents. If it can't find that, it will look for a match with any of the other types it supports. If it finds no matches, you'll get an error about not finding a matching function. If it finds multiple matches, you'll get an error about an ambiguous function (which can be resolved via a static_cast). If it finds one match, then it will implicitly do a conversion (unless that function is marked as explicit).
In the following program.
I am getting the following error
saumitra@saumitra-VirtualBox:~$ make fehcel
g++ fehcel.cpp -o fehcel
fehcel.cpp: In member function ‘Celsius::operator Fahrenheit()’:
fehcel.cpp:13:24: error: return type ‘class Fahrenheit’ is incomplete
operator Fahrenheit() {return Fahrenheit(m_celsius*9/5+32.0);}
^
fehcel.cpp:13:61: error: invalid use of incomplete type ‘class Fahrenheit’
operator Fahrenheit() {return Fahrenheit(m_celsius*9/5+32.0);}
^
fehcel.cpp:2:7: note: forward declaration of ‘class Fahrenheit’
class Fahrenheit;
^~~~~~~~~~
<builtin>: recipe for target 'fehcel' failed
make: *** [fehcel] Error 1
Even when I have forward declared Fahrenheit class why doesn't the compiler know the return type of the operator function ? Is there any fix ?I tried using header files but wouldn't work either way!
I moved the definitions of the operator member functions outside and now it works!
This was caused by a circular dependency -- The implementation of some Celsius functions depend on Fahrenheit, and vice versa. Moving the relevant member function definitions below the class definitions allows each class to at least be fully declared by the point of function definition.
Hi Alex,
I didn't get one thing here. All examples so far were all about conversion from the given class(e.q. Dollars) to another one(e.q. Cents). Maybe I didn't get something really obvious, but what about conversion from another class (Cents) to the given(Dollars)? Surely, it's possible to implement typecast for such reason in Cents class, but it's probably not the best solution (e.q. we cannot reimplement standart classes like vector, so how to cast vector to smth?). Whether it is possible to convert in such way? Thanks in advance and sorry for typos.
Generally if you want to convert from another class to your class, you'd do that by providing a constructor that takes the appropriate type by const reference. So, for example, if you wanted to convert another class (Cents) to your class (Dollars), you'd provide a Dollars constructor that took a Cents by const reference. This will allow the compiler to convert a Cents into a Dollars.
But the main point of using casting in my particular case is implicit casting from one type to another, so constructor won't help here. I checked the fact that C# allows such thing, so somehow expected that C++ also has appropriate syntax. Searched through many resourses, but haven't found anything about it. Maybe you do have some ideas?
Conversion from another class to the given class: use a constructor
Conversion from a given class to another class: use an overloaded typecast
Both of these will work implicitly.
For example, if you want to be able to cast a vector to smth, then your smth class would have a vector constructor allowing it to convert a vector into a smth. If you want to convert a smth into a vector, then your smth class can have an overloaded typecast.
I'm not clear on why this won't work for your particular case.
I've just got that constructor can work implicitly.
Thanks!
Alex,
The first example has this in the body of the printInt () function:
Shouldn’t that be:
I like how this method, at least for the example given, makes it unnecessary to make overloaded friend functions for operator<<. Very cool!
In dollars cent example you use printcents() which print 900 why not printint()? Printcents () only find cent object so way is goes for serching int.....
In this particular case, because Cents has a cast to int, and because printCents() and printInt() do the same thing, you could use printInt() instead and let the compiler implicitly convert your Cents to an Int.
Hi,
Can somebody please tell me how to overload standard datatype like float to user defined datatype. I have function f2my(float f, mttype* m) which converts float to mytype. I want to overload all floats in my code with mytype i.e. 'float a' should be 'mytype a'.
Thank you.
I don't think this is possible.
You mean it's not possible to convert a (float) to a (user-defined data type)? Tell me if I read it wrong.
That part is fine and doable. The part where he wants to replace float with mytype I don't think is possible without doing a search/replace.
In the code snippet above the first Dollar class code example, you wrote:
"We can now also explicitly cast our Cents variable to an int:
Cents cents(7);
int cents = static_cast<int>(cents);"
How can you have two identifiers of the same name, but of two different types? Did you mean to use a different identifier for the int variable?
Typo on my part. I've changed the name of the int from cents to c to avoid the naming conflict.
Hi Alex,
What if we not only have int dollars/cents but also double? For example dollars(9) or dollars(9.3)
If this is a valid use case, then the Dollars class would need to modified to handle fractional dollars.
At the end of this chapter, there's a quiz question around implementing fixed point numbers that would be a good way to handle this (it's not quite as easy as it seems).
We have overloade typecast to int here, so cents get converted to int. But , what if we have also overloaded << operator for the Cents class??
I tried it and it looks like overloaded << was used.
An overloaded operator<< for class Cents is considered a better match than an implicit conversion to integer, so the overloaded operator<< will be used. Of course, if we'd overloaded operator<<, there wouldn't much point in even having a printCents() function.
Got it. Thanks , Alex.
Hi Alex,
letting you know that there is a typo
should be
this happens in the 2nd and 3rd instance of your Cents class code
Fixed. Thanks!
Hi Alex:
In the last example above,why can't
pass value by reference like this?
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).
thank you for this explanation
Thanks for your help.
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.
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>>.
Hi Alex,
operator std::string() { return std::to_string(42); } should be operator std::string() { return std::to_string(m_cents) };, It can even be mentioned that even though the conversion operator does not take any parameters but all the members of the passed in object(in this case cents1) can be referenced in the conversion operator as it would be more descriptive as part of this lesson.
Comment fixed. Thanks for pointing out my error!
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.
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.
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!
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?
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.
Dear, Steiner. Can you give me please show me your code or explain how to make mutual type congestion in this example(I just now have a problem with this).
pretty easy actually:
1. copy the above code into their respective files
2. replace #includes "Meters.h" in Millimeters.h with class Meters
3. replace #includes "Millimeters.h" in Meters.h with class Millimeters
4. delete the std::cout<< functions in main.cpp
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?
Yes, in order for that example to compile, you will have to declare Cents before Dollar. Otherwise the compiler will have no idea what a Cents is.