How do conversion operators work in C++?

Consider this simple example:

template <class Type>
class smartref {
public: smartref() : data(new Type) { } operator Type&(){ return *data; }
private: Type* data;
};
class person {
public: void think() { std::cout << "I am thinking"; }
};
int main() { smartref<person> p; p.think(); // why does not the compiler try substituting Type&?
}

How do conversion operators work in C++? (i.e) when does the compiler try substituting the type defined after the conversion operator?

0

7 Answers

Some random situations where conversion functions are used and not used follow.

First, note that conversion functions are never used to convert to the same class type or to a base class type.

Conversion during argument passing

Conversion during argument passing will use the rules for copy initialization. These rules just consider any conversion function, disregarding of whether converting to a reference or not.

struct B { };
struct A { operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!

Argument passing is just one context of copy initialization. Another is the "pure" form using the copy initialization syntax

B b = A(); // called!

Conversion to reference

In the conditional operator, conversion to a reference type is possible, if the type converted to is an lvalue.

struct B { };
struct A { operator B&() { static B b; return b; }
};
int main() { B b; 0 ? b : A(); } // called!

Another conversion to reference is when you bind a reference, directly

struct B { };
struct A { operator B&() { static B b; return b; }
};
B &b = A(); // called!

Conversion to function pointers

You may have a conversion function to a function pointer or reference, and when a call is made, then it might be used.

typedef void (*fPtr)(int);
void foo(int a);
struct test { operator fPtr() { return foo; }
};
int main() { test t; t(10); // called!
}

This thing can actually become quite useful sometimes.

Conversion to non class types

The implicit conversions that happen always and everywhere can use user defined conversions too. You may define a conversion function that returns a boolean value

struct test { operator bool() { return true; }
};
int main() { test t; if(t) { ... }
}

(The conversion to bool in this case can be made safer by the safe-bool idiom, to forbid conversions to other integer types.) The conversions are triggered anywhere where a built-in operator expects a certain type. Conversions may get into the way, though.

struct test { void operator[](unsigned int) { } operator char *() { static char c; return &c; }
};
int main() { test t; t[0]; // ambiguous
}
// (t).operator[] (unsigned int) : member
// operator[](T *, std::ptrdiff_t) : built-in

The call can be ambiguous, because for the member, the second parameter needs a conversion, and for the built-in operator, the first needs a user defined conversion. The other two parameters match perfectly respectively. The call can be non-ambiguous in some cases (ptrdiff_t needs be different from int then).

Conversion function template

Templates allow some nice things, but better be very cautious about them. The following makes a type convertible to any pointer type (member pointers aren't seen as "pointer types").

struct test { template<typename T> operator T*() { return 0; }
};
void *pv = test();
bool *pb = test();
12

The "." operator is not overloadable in C++. And whenever you say x.y, no conversion will automatically be be performed on x.

0

Conversions aren't magic. Just because A has a conversion to B and B has a foo method doesn't mean that a.foo() will call B::foo().

The compiler tries to use a conversion in four situations

  1. You explicitly cast a variable to another type
  2. You pass the variable as an argument to a function that expects a different type in that position (operators count as functions here)
  3. You assign the variable to a variable of a different type
  4. You use the variable copy-construct or initialize a variable of a different type

There are three types of conversions, other than those involved with inheritance

  1. Built-in conversions (e.g. int-to-double)
  2. Implicit construction, where class B defines a constructor taking a single argument of type A, and does not mark it with the "explicit" keyword
  3. User-defined conversion operators, where class A defines an operator B (as in your example)

How the compiler decides which type of conversion to use and when (especially when there are multiple choices) is pretty involved, and I'd do a bad job of trying to condense it into an answer on SO. Section 12.3 of the C++ standard discusses implicit construction and user-defined conversion operators.

(There may be some conversion situations or methods that I haven't thought of, so please comment or edit them if you see something missing)

Implicit conversion (whether by conversion operators or non-explicit constructors) occurs when passing parameters to functions (including overloaded and default operators for classes). In addition to this, there are some implicit conversions performed on arithmetic types (so adding a char and a long results in the addition of two longs, with a long result).

Implicit conversion does not apply to the object on which a member function call is made: for the purposes of implicit conversion, "this" is not a function parameter.

The compiler will attempt one(!) user-defined cast (implicit ctor or cast operator) if you try to use an object (reference) of type T where U is required.

The . operator, however, will always try to access a member of the object (reference) on its left side. That's just the way it's defined. If you want something more fancy, that's what operator->() can be overloaded for.

2

You should do

((person)p).think();

The compiler doesn't have the information for automatically casting to person, so you need explicit casting.

If you would use something like

person pers = p;

Then the compiler has information for implicit casting to person.

You can have "casting" through constructors:

class A
{
public: A( int );
};
A a = 10; // Looks like a cast from int to A

These are some brief examples. Casting (implicit, explicit, etc) needs more to explain. You can find details in serious C++ books (see the questions about C++ books on stack overflow for good titles, like this one).

1

//Virtual table Fuction(VFT)

#include <iostream>
using namespace std;
class smartref {
public:
virtual char think() { }//for Late bindig make virtual function if not make virtual function of char think() {} then become early binding and pointer call this class function smartref() : data(new char) { } operator char(){ return *data; }
private: char* data;
};
class person:public smartref
{
public: char think() { std::cout << "I am thinking"; }
};
int main() { smartref *p;//make pointer of class person o1;//make object of class p=&o1;//store object address in pointer p->think(); // Late Binding in class person
return 0;
}
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like