I am studying operator overloading, there are some parts that are difficult to understand.
See this example code.
class A {
private:
char a;
int b;
double c;
public:
A(char _a = 'a', int _b = 99, double _c = 1.618) :a(_a), b(_b), c(_c){
}
public:
operator char() const {
cout << "operator char() called" << endl;
return this->a;
}
operator int() const {
cout << "operator int() called" << endl;
return this->b;
}
operator double() {
cout << "operator double() called" << endl;
return this->c;
}
};
int main(void) {
A a;
char b = a;
int c = a;
double d = a;
printf("%c\n", b);
printf("%d\n", c);
printf("%f\n", d);
return 0;
}
I made this code to test for type conversion operator and expected that the appropriate function would be called for each type of data.
But the result is..
operator double() called
operator double() called
operator double() called
<-- strange character is gone on board!
1
1.618000
I can not understand why the results are not as follows.
operator char() called
operator int() called
operator double() called
a
99
1.618
Why is double operator called when converting to char and int?
Have a good day! :)
You forgot the const on the double conversion operator:
operator double() const { // <---------------------------
cout << "operator double() called" << endl;
return this->c;
}
};
As in your example a is not const, the double conversion is the best match. If you fix that you get the expected output.
Live example
...some opinion based PS:
I didnt find what the core guidelines say about conversion operators, but if I had to make up a guideline for conversion operators it would be: Avoid them. If you use them, make them explicit. The surprising effects of implicit conversion outweigh the benefits by far.
Just as an example, consider std::bitset. Instead of offering conversion operators it has to_string, to_ulong and to_ullong. It is better to have your code explicit. A a; double d = a; is a little bit mysterious. I would have to look at the class definition to get an idea of what is really going on. On the other hand A a; double d = a.as_double(); can do the exact same thing, but is way more expressive.
Yea so the problem is, that you made all operators const except for the double operator. I am still a bit surprised because this const just means that the operator call does not modify the class members. Still it seems that only the double operator is called for all 3. I would all 3 op's make const and then it will work properly.
If someone has an explanation why this happens, I would also like to know.
Cheers.
operator char() const { // here is const
cout << "operator char() called" << endl;
return this->a;
}
operator int() const { // here is const
cout << "operator int() called" << endl;
return this->b;
}
operator double() { // here is no const
cout << "operator double() called" << endl;
return this->c;
}
Related
How conversion constructor is getting used in following program
#include <iostream>
#include <cmath>
using namespace std;
class Complex
{
private:
double real;
double imag;
public:
// Default constructor
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i)
{}
// magnitude : usual function style
double mag()
{
return getMag();
}
// magnitude : conversion operator
operator double ()
{
return getMag();
}
private:
// class helper to get magnitude
double getMag()
{
return sqrt(real * real + imag * imag);
}
};
int main()
{
// a Complex object
Complex com(3.0, 4.0);
// print magnitude
cout << com.mag() << endl;
// same can be done like this
cout << com << endl;
}
I am not able to understand that why statement "cout << com << endl;"calling funtion mag(). Please help. if I change function mag() to mag (int i) then also , same output.
mag() is never called. Your double conversion operator calls getMag() which is why it appears that mag() is being called.
When you call cout << com << endl there is no overloaded operator << for your Complex class. This causes the compiler to look for implicit conversions to something operator << supports. Your complex class has overloaded the double conversion operator here:
operator double ()
{
return getMag();
}
As you can see the double conversion operator calls getMag() and this it why it appears that mag() is being called.
The statement
cout<<com<<endl;
does not call Complex::mag(). It is calling the convertion operator operator double();
The class has conversion operator
operator double ()
{
return getMag();
}
So if you have an object of type Complex named com then in statement
std::cout << com << std::endl;
there will be called the conversion operator that converts object com to an object of type double.
If you will declare the operator with specifier explicit
explicit operator double ()
{
return getMag();
}
then the statement above will not compile.
Let's say I have defined a class with an internal + operator and also an external + operator;
class MyClass {
public:
MyClass operator +();
};
MyClass operator +(const MyClass& a);
If in my main program I call
MyClass a;
MyClass b = +a;
What is being called, this (internal):
a.operator +()
or this (external)?:
operator +(a)
The same question for binary operators.
The member function is chosen: it can bind directly to the expression a, while the non-member function needs to convert MyClass to const MyClass before binding to the reference parameter. So calling the member involves a better conversion sequence, making that the best overload.
If you removed const from the non-member, or added const to the member, then both would be equally viable; you should get an error saying that the overload is ambiguous.
By fixing some ambiguities in the code, and doing some printing, the following code will give the answer Internal operator.
class MyClass {
public:
MyClass operator+() {
std::cout << "Internal operator." << std::endl;
return *this;
};
};
MyClass operator+(const MyClass& a) {
std::cout << "External operator" << std::endl;
return a;
}
int main() {
MyClass a, b;
b = +a;
return 0;
}
The internal operator is used because you cannot overload an operator with the same arguments once it already exists: you cannot define a + operator for strings that does something crazy because there already is one that concatenates them. That's the same case. You have a + operator defined inside the class, so the identical one( as function prototype ) becomes useless.
I did a little test myself:
#include <iostream>
#include "MyClass.h"
using namespace std;
MyClass operator +(const MyClass& a, const MyClass& b)
{ cout << "external" << endl; return a; }
int main() {
MyClass foo, bar;
foo + bar;
return 0;
}
class MyClass {
public:
MyClass operator+(const MyClass& a) { cout << "internal" << endl; return a; }
};
The output of the program was "internal".
My conclusion is that if there is an internal operator that fits the operation it will be the one taken. I can't see a way to make the external one to be taken if the internal fits better. But I must point out that I only did a little test and haven't learned somewhere that this is the 100% answer.
If the following test-programm
#include <iostream>
class A {
public:
A() {}
explicit operator bool() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return true;
}
// explicit operator bool() {
// std::cout << __PRETTY_FUNCTION__ << std::endl;
// return true;
// }
const operator int() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
operator int() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
};
int main() {
A a;
if (a) {
std::cout << "bool()" << std::endl;
}
if (a + 0) {
std::cout << "int()" << std::endl;
}
}
is run, the output is
int A::operator int()
bool()
int A::operator int()
int()
and not
bool A::operator _Bool()
bool()
int A::operator int()
int()
what I expected (and what you get if you uncomment the commented parts).
So the question is what are the rules giving the conversion to non-const-int precedence over converting to const-bool?
When performing overload resolution on a reference binding, the less cv-qualified type is preferred. This is discussed in 13.3.3.2p3, with the example given:
struct X {
void f() const;
void f();
};
void g(const X& a, X b) {
a.f(); // calls X::f() const
b.f(); // calls X::f()
}
Note that binding an object to the implicit object parameter of a member function (13.3.1.1.1p2) is a reference binding (13.3.3.1.4).
Conversion operators are treated as member functions (13.3.1.5) for the purposes of overload resolution (13.3p2). Contextual conversion to bool has the semantics of initialization (4p4).
Importantly, any conversion required on the return type of the conversion operator is considered only after considering overload resolution between the conversion operators themselves (13.3.3p1).
The solution is to ensure that all conversion operators have the same const-qualification, especially to scalar type.
what are the rules giving the conversion to non-const-int precedence over converting to const-bool?
Using const member function of a non-const object, requires conversion of a non-const object into const-object. That is why operator int() has a better match over operator bool() const.
To make it a bit clearer, if you would remove int operators, what really happens with the first bool context (first if) is this :
if ( const_cast<const A&>(a).operator bool() ) {
Instead, what happens is this :
if ( a.operator int() )
I tried creating a class with one operator bool and one operator void*, but the compiler says they are ambigous. Is there some way I can explain to the compiler what operator to use or can I not have them both?
class A {
public:
operator void*(){
cout << "operator void* is called" << endl;
return 0;
}
operator bool(){
cout << "operator bool is called" << endl;
return true;
}
};
int main()
{
A a1, a2;
if (a1 == a2){
cout << "hello";
}
}
The problem here is that you're defining operator bool but from the sounds of it what you want is operator ==. Alternatively, you can explicitly cast to void * like this:
if ((void *)a1 == (void *)a2) {
// ...
}
... but that's really bizarre. Don't do that. Instead, define your operator == like this inside class A:
bool operator==(const A& other) const {
return /* whatever */;
}
You could call the operator directly.
int main()
{
A a1, a2;
if (static_cast<bool>(a1) == static_cast<bool>(a2)){
cout << "hello";
}
}
In this case, though, it looks like you should define operator==() and not depend on conversions.
I'm trying to overload the comma operator with a non-friend non-member function like this:
#include <iostream>
using std::cout;
using std::endl;
class comma_op
{
int val;
public:
void operator,(const float &rhs)
{
cout << this->val << ", " << rhs << endl;
}
};
void operator,(const float &lhs, const comma_op &rhs)
{
cout << "Reached!\n"; // this gets printed though
rhs, lhs; // reversing this leads to a infinite recursion ;)
}
int main()
{
comma_op obj;
12.5f, obj;
return 0;
}
Basically, I'm trying to get the comma operator usable from both sides, with a float. Having a member function only allows me to write obj, float_val, while having an additional helper non-friend non-member function allows me to write float_val, obj; but the member operator function doesn't get called.
GCC cries:
comma.cpp: In function ‘void operator,(const float&, const comma_op&)’:
comma.cpp:19: warning: left-hand operand of comma has no effect
comma.cpp:19: warning: right-hand operand of comma has no effect
Note:
I realise that overloading operators, that too to overload comma op., Is confusing and is not at all advisable from a purist's viewpoint. I'm just learning C++ nuances here.
void operator,(const float &rhs)
You need a const here.
void operator,(const float &rhs) const {
cout << this->val << ", " << rhs << endl;
}
The reason is because
rhs, lhs
will call
rhs.operator,(lhs)
Since rhs is a const comma_op&, the method must be a const method. But you only provide a non-const operator,, so the default definition will be used.