Please help me understand how exactly the conversion operators in C++ work.
I have a simple example here which I am trying to understand, though it is not very clear how the conversion actually happens by the compiler.
class Example{
public:
Example();
Example(int val);
operator unsigned int();
~Example(){}
private:
int itsVal;
};
Example::Example():itsVal(0){}
Example::Example(int val):itsVal(val){}
Example::operator unsigned int (){
return (itsVal);
}
int main(){
int theInt = 5;
Example exObject = theInt; // here
Example ctr(5);
int theInt1 = ctr; // here
return 0;
}
You can walk through that code with a debugger (and/or put a breakpoint on each of your constructors and operators) to see which of your constructors and operators is being invoked by which lines.
Because you didn't define them explicitly, the compiler also created a hidden/default copy constructor and assignment operator for your class. You can define these explicitly (as follows) if you want to use a debugger to see where/when they are being called.
Example::Example(const Example& rhs)
: itsVal(rhs.itsVal)
{}
Example& operator=(const Example& rhs)
{
if (this != &rhs)
{
this->itsVal = rhs.itsVal;
}
return *this;
}
int main() {
int theInt = 5;
/**
* Constructor "Example(int val)" in effect at the statement below.
* Same as "Example exObject(theInt);" or "Example exObject = Example(theInt);"
*/
Example exObject = theInt; // 1
Example ctr(5);
/**
* "operator unsigned int()" in effect at the statement below.
* What gets assigned is the value returned by "operator unsigned int()".
*/
int theInt1 = ctr; // 2
return 0;
}
At statement 1 the constructor Example(int val) is called. Declare it as explicit Example(int val) and you will get a compile time error i.e. no implicit conversion will then be allowed for this constructor.
All single argument constructors are called implicitly if the assigned value is of their respective argument type. Using the explicit keyword before single argument constructors disables implicit constructor calling and hence implicit conversion.
If the constructor was declared as explicit i.e. explicit Example(int val) then the following would happen for each statement.
Example exObject(theInt); // Compile time error.
Example exObject = theInt; // Compile time error.
Example exObject(Example(theInt)); // Okay!
Example exObject = Example(theInt); // Okay!
Also note that in case of implicit constructor call and hence implicit conversion the assigned value is an rvalue i.e. an un-named object implicitly created using an lvalue (theInt) which tells us that in case of implicit conversion the compiler converts
Example exObject = theInt;
to
Example exObject = Example(theInt);
So (in C++11) don't expect the lvalue constructor to be called seeing that you are using an lvalue i.e. a named value theInt for assignment. What gets called is the rvalue constructor since the assigned value is actually the un-named object created using the lvalue. However, this applies if you have both lvalue and rvalue versions of the constructor.
At statement 2 operator unsigned int() is called. Simply consider it as a normal function call with a weird name and the fact that it can get called automagically when an implicit conversion happens. The value returned by that function is the value assigned in the expression. And since in you implementation the value returned is an int it correctly gets assigned to int theInt1.
To be precise operator unsigned int() overloads () operator which is the cast operator. In your case it's overloaded for int hence whenever an object of Example class is assigned to an int the implicit type casting from Example to int takes place and hence operator unsigned int() gets called. Therefore,
int theInt1 = ctr;
is equivalent to
int theInt1 = (int)ctr;
Example exObject = theInt; // implicitly created copy constructor takes place
// object implicitly created from int and then copied
// it is like
Example exObject = Example(theInt);
// so it uses sequence
// Example(int) -> Example(const Example&)
int theInt1 = ctr; // operator int()
If you compiler supports copy constructor optimization and return value optimization you won't notice
Example(const Example&)
execution, but you can declare copy constructor to be private to understand what I am talking about.
Example exObject = theInt; // here
This uses implicit conversion of int to Example, effected by the non-explicit constructor which accepts an int.
This also requires the availability of copy constructor for Example, even though the compiler is allowed to omit copying the instance.
int theInt1 = ctr; // here
This uses implicit conversion of Example to unsigned int, provided by the cast operator.
Cast operators are normally avoided, since they tend to lead to confusing code, and you can mark single-argument constructors explicit, to disable implicit conversions to your class type. C++0x should add also the possibility to mark conversion operators explicit (so you'd need a static_cast to invoke them? - my compiler doesn't support them and all web resources seem to be concentrating on explicit conversion to bool).
Related
What is the "operator int" function below? What does it do?
class INT
{
int a;
public:
INT(int ix = 0)
{
a = ix;
}
/* Starting here: */
operator int()
{
return a;
}
/* End */
INT operator ++(int)
{
return a++;
}
};
The bolded code is a conversion operator. (AKA cast operator)
It gives you a way to convert from your custom INT type to another type (in this case, int) without having to call a special conversion function explicitly.
For example, with the convert operator, this code will compile:
INT i(1234);
int i_2 = i; // this will implicitly call INT::operator int()
Without the convert operator, the above code won't compile, and you would have to do something else to go from an INT to an int, such as:
INT i(1234);
int i_2 = i.a; // this wont compile because a is private
operator int() is a conversion operator, which allows this class to be used in place of an int. If an object of this type is used in a place where an int (or other numerical type) is expected, then this code will be used to get a value of the correct type.
For example:
int i(1);
INT I(2); // Initialised with constructor; I.a == 2
i = I; // I is converted to an int using `operator int()`, returning 2.
First things first:
$12.3.1/1 - "A constructor declared
without the function-specifier
explicit specifies a conversion from
the types of its parameters to the
type of its class. Such a constructor
is called a converting constructor."
In your example, INT is a User Defined class that has a converting constructor from 'int'.
Therefore the following code is well-formed:
INT i(1024); // direct initialization syntax
This means that you can get an INT object from an integer. However what does one do, if the INT object has to be converted back to an integer? Transitivity?
One can say that the class INT can provide a member function to return the encapsulated integer member
int x = i.geta();
This however is not very intuitive and is not a standardized approach. Also it is not intuitive when it comes to how built-in types work in such situations.
int z = 0;
int y1 = z; // copy initialization or
int y2(z); // direct initialization
double d = (int )z; // explicit cast
Therefor the Standard allows for such standardization and intuitiveness of converting User Defined Types by saying:
$12.3/2 - "A member function of a
class X having no parameters with a
name of the form [...]
operator conversion-type-id
[...]specifies a conversion from X to the
type specified by the
conversion-type-id. Such functions are
called conversion functions. No return
type can be specified. If a conversion
function is a member function, the
type of the conversion function
(8.3.5) is “function taking no
parameter returning
conversion-type-id”.
This makes all of the following well-formed and retains harmony with the way built-in types work is
int y1 = i; // copy initialization or
int y2(i); // direct initialization
double d = (int )i; // explicit cast
It looks like it make an INT class which behaves a little like the regular int, just that some other operators are not yet defined.
Is this a homework problem?
Seems like it's a question from a classroom, so I'll invite you to check the documentation on how to create a class.
class Foo
{
public
Foo() {} // Constructor
Foo operator++ {} // Operation ++ on foo like:
// Foo foo;
// foo++;
};
I am on the task to migrate the concept of error handling in a C++ class library. Methods that previously simply returned bool (success/fail) shall be modified to return a Result object which conveys a machine readable error code and a human readable explanation (and some more which does not matter here).
Walking through thousands of lines of code is error prone, therefore I try to get the best support from the compiler for this task.
My result class has - among other member methods - a constructor that constructs the result from a code and an assignment operator for the code:
class Result
{
public:
typedef unsigned long ResultCode;
explicit Result(ResultCode code); // (1)
Result& operator=(ResultCode code); // (2)
};
Remark: I would usually use an enum class for ResultCode which would solve my problems, but this is not an option. This is because the major design objective was to use Result in different libraries, each of which shall define its own set of result codes without requiring one big header file that defines all possible result codes for all libraries. In fact, each class shall be able to define local result codes so that the list of possible result codes can be obtained from the classes header. Thus the codes cannot be enumerated in Result, they must be defined by the classes using the Result class.
To avoid implicit conversions on
return true;
Statements in the client code, the constructor has been declared explicit. But in nesting method calls, another problem occurs. Say, I have a method
bool doSomething()
{
return true;
}
Which I am using in a function that returns a Result object. I want to forward result codes of nested calls
Result doSomethingElse
{
Result result = doSomething();
return result;
}
With the current implementation of Result's assignment operator, this is not going to give me a compiler error - the boolean return value of doSomething() is implicitly converted to unsigned long.
As I have read in the C++ documentation, only constructors and conversion operators may be declared explicit.
My questions
Why is explicit not allowed for assignment operators or other methods? IMO it would make a lot of sense to allow any method to be explicit as well.
Are there other solutions to prevent implicit type conversion for the assignment operator?
Your problem isn't in the class Result: you are explicitly creating a new instance of it, after all; explicit cannot forbid it.
I don't think you can forbid the implicit promotion bool -> long.
You can work around it. One way is to make ResultCode not be an integer type. then, it could have an explicit constructor. Something like
class ResultCode
{
unsigned long m_code;
public:
explicit ResultCode(unsigned long code) : m_code(code) {}
operator unsigned long () { return m_code; }
};
would allow you to use ResultCode anywhere you can use a unsigned int and create it as ResultCode res = 5 or return ResultCode(5) but not call a function expecting a ResultCode (such as the Result constructor!) with anything which is not a ResultCode already, nor do something like return 5 if the function must return a ReturnCode.
Otherwise you can use template overloadng to 'catch' anything not being an unsigned int and force it to be an error
typedef unsigned long ResultCode;
class Result
{
ResultCode m_par;
public:
template<typename T>
Result(T param) { static_assert(false); }
template<>
Result(ResultCode par): m_par(par) {}
};
int main()
{
ResultCode a = 5; //ok
//unsigned long a = 6; //also ok
//bool a = true; //error!
//int a = 7; //also error!!
Result b(a);
}
With the current implementation of Result's assignment operator, this is not going to give me a compiler error - the boolean return value of doSomething() is implicitly converted to unsigned long.
With respect to the code you posted; it does result in an error error: no viable conversion from 'bool' to 'Result', see here.
A minimal example showing the behaviour you see in the code would be required. There is likely other constructors or type with conversion in the actual code that have a material effect on your code.
On the explicitly asked questions...
Why is explicit not allowed for assignment operators or other methods?
explicit is only allowed where implicit conversion can take place, i.e. where the compiler would attempt to generate the conversion for you (there is a special case for bool). Such conversions are constructors and the conversion (or casting operators).
Marking the constructor or conversion operator as explicit prevents the compiler from making the conversions, hence, if you require the conversion, you need to be explicit about it - as a general motivation for why this is done, it makes the code more explicit in what it does. There is a trade-off, so judicious use should be applied in both cases. The general advice is to favour explicit when in doubt.
For example;
struct Result {
Result(long src); // can be marked explicit
operator long() const; // can be marked explicit
};
Are there other solutions to prevent implicit type conversion for the assignment operator?
The assignment operator has a particular for Result& operator=(Result&);. In the assignment itself, there are no conversion. To prevent the implicit creation of a Result for the assignment, the constructor(s) need to be marked explicit.
To prevent the Result from being created from a ResultCode, you can either not declare the method, or mark it as deleted;
Result& operator=(ResultCode code) = delete;
How it comes that operation like foo = int can be done by both foo(int) (conversion constructor) and foo::operator=(int) (overloaded assignment operator)? When one be called instead of other (maybe one is rudimentary)?
#include <iostream>
class foo
{
public:
foo(){}
foo(int r_value)
{
std::cout << "\nfoo::foo(int)\n";
}
void operator=(int r_value)
{
std::cout << "\nfoo::operator=(int)\n";
}
};
int main()
{
foo f;
f = 57;
return 0;
}
Code above makes operator=(int) to run when both exist and foo(int) if operator=(int) is commented (or opposite).
This is basic overload resolution. Both overloads are viable:
Binding 57 to foo::operator=(int) (exact match)
Implicitly converting 57 to foo via the converting constructor, and binding the temporary foo object to the implicitly defined foo::operator=(foo const &).
Since the latter requires more conversions than the former, it is a less-good match, and the former overload is chosen.
You can still achieve the second call by making it explicit:
f = foo(57); // binds perfectly to foo::operator=(foo const &)
The full set of rules for overload resolution are rather long and involved, but in individual cases like this the answer is straight-forward. See 13.3 ([over.match]) for the full, gory details, though.
There is a difference:
foo a = 10;
Calls foo::foo(int)
foo a;
a = 10;
Calls foo::operator=(int) in a
Both of the implementations are different. The first is a Constructor and the second is an assignment. The Use cases varies, and each of them would be called accordingly based on the use case.
Use Case
The Constructor is called foo::foo(int)
foo f = 57;
The Assignment is called foo::operator=(int)
foo f;
f = 57;
Note
Using assignment in the above use case and in your example has more overhead, as it called the default constructor along with the assignment.
For this statement
f = 57;
the compiler at first considers all functions operator =. There are two such functions: the explicitly defined by you and the copy assignment operator implicitly defined by the compiler. The first one is the best suitable function. So it is called.
If you will comment this assignment operator then the compiler has only one function operator =. It is the implicitly defined copy assignment operator. But it can not be applied directly. So the compiler seeks a way to convert supplied argument to type foo. And it can do this by calling the conversion construuctor.
This example is from "Thinking in C++", I have one question regarding compiler synthesizing the operator conversion function.
Question
When object of class Four is passed (in the function call f()), the overload operation () is called. But I am not able to make out the logic used (compiler synthesizes the operation call) by compiler to achieve this conversion.
At max, I can expect explicit conversion behavior, like
1. obj3 = (Three)obj4;
2. obj3 = Three(obj4);
3. obj3 = static_cast<Three> (obj4);
Now for any one of the above conversion - how does the compiler synthesize,
(Three) obj4.operator()?
May be I am missing some major point.
Example
//: C12:Opconv.cpp
// Op overloading conversion
class Three {
int i;
public:
Three(int ii = 0, int = 0) : i(ii) {}
};
class Four {
int x;
public:
Four(int xx) : x(xx) {}
operator Three() const { return Three(x); }
};
void g(Three) {}
int main() {
Four four(1);
g(four);
g(1); // Calls Three(1,0)
} ///:~
First of all it is not operator() which you have provided, it is operator Three. This operator tells the compiler how to convert an object of class Four to an object of class Three. In g(four) call compiler is using this operator since the function g is expecting an argument of type Three. Since there is an conversion available compiler is using it. In the second case, since the constructor of Three is not declared as explicit and it is possible to construct a object of class Three using a single integer (using Three constructor) compiler is using that constuctor to create an object of the class Three so that function g can be called.
First of all, class Four does not contain an operator(), but it does have an operator Three(), which is a conversion operator.
In the line
g(four);
the compiler needs to convert four to an object of class Three and synthesises a call to operator Three() to perform that conversion.
The synthesised conversion is equivalent to
g(four.operator Three());
Let say I have a object. I'm assigning that to an integer.
MyClass obj1 = 100;//Not valid
Let's say, I have a parameterized constructor which accepts an integer.
MyClass(int Num)
{
// .. do whatever..
}
MyClass obj1 = 100;//Now, its valid
Likewise on any circumstance, does the vice-versa becomes valid?!.
eg) int Number = obj1;//Is it VALID or can be made valid by some tweeks
EDIT:
I found this to be possible using Conversion Functions.
Conversion functions are often called "cast operators" because they (along with constructors) are the functions called when a cast is used.
Conversion functions use the following syntax:
operator conversion-type-name ()
eg) Many have explained it neatly below
Yes, provided that the object is implicitly convertible to an int, either directly or through an intermediate object.
E.g. If your class have a conversion operator int it would work:
MyClass
{
public:
operator int() const { return 200; }
};
C++ constructors that have just one parameter automatically perform implicit type conversion. This is why conversion from int to MyClass works. To create conversion from MyClass to int you have to define operator int() inside MyClass.
Yes you can, using user defined conversions.
In your MyClass, define operator int()
So
class MyClass
{
int myVal;
public:
operator int() { return myVal;}
}
Yes, you need to add a conversion operator to MyClass
operator int();
MyClass is not an integer therefore you can't do int Number = obj1;
You should have a method or operator(stated by others) in MyClass that returns an int. (int number = obj1.GetNum();)