Standard conversions
The C++ language defines conversions between its fundamental types. It also defines conversions for pointer, reference, and pointer-to-member derived types. These conversions are called standard conversions.
This section discusses the following standard conversions:
Integral promotions
Integral conversions
Floating conversions
Floating and integral conversions
Arithmetic conversions
Pointer conversions
Reference conversions
Pointer-to-member conversions
Note
User-defined types can specify their own conversions. Conversion of user-defined types is covered in Constructors and Conversions.
The following code causes conversions (in this example, integral promotions):
long long_num1, long_num2;
int int_num;
// int_num promoted to type long prior to assignment.
long_num1 = int_num;
// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;
The result of a conversion is an l-value only if it produces a reference type. For example, a user-defined conversion declared as operator int&()
returns a reference and is an l-value. However, a conversion declared as operator int()
returns an object and isn't an l-value.
Integral promotions
Objects of an integral type can be converted to another wider integral type, that is, a type that can represent a larger set of values. This widening type of conversion is called integral promotion. With integral promotion, you can use the following types in an expression wherever another integral type can be used:
Objects, literals, and constants of type
char
andshort int
Enumeration types
int
bit fieldsEnumerators
C++ promotions are "value-preserving," as the value after the promotion is guaranteed to be the same as the value before the promotion. In value-preserving promotions, objects of shorter integral types (such as bit fields or objects of type char
) are promoted to type int
if int
can represent the full range of the original type. If int
can't represent the full range of values, then the object is promoted to type unsigned int
. Although this strategy is the same as the one used by Standard C, value-preserving conversions don't preserve the "signedness" of the object.
Value-preserving promotions and promotions that preserve signedness normally produce the same results. However, they can produce different results if the promoted object appears as:
An operand of
/
,%
,/=
,%=
,<
,<=
,>
, or>=
These operators rely on sign for determining the result. Value-preserving and sign-preserving promotions produce different results when applied to these operands.
The left operand of
>>
or>>=
These operators treat signed and unsigned quantities differently in a shift operation. For signed quantities, a right shift operation propagates the sign bit into the vacated bit positions, while the vacated bit positions are zero-filled in unsigned quantities.
An argument to an overloaded function, or the operand of an overloaded operator, that depends on the signedness of the operand type for argument matching. For more information about defining overloaded operators, see Overloaded operators.
Integral conversions
Integral conversions are conversions between integral types. The integral types are char
, short
(or short int
), int
, long
, and long long
. These types may be qualified with signed
or unsigned
, and unsigned
can be used as shorthand for unsigned int
.
Signed to unsigned
Objects of signed integral types can be converted to corresponding unsigned types. When these conversions occur, the actual bit pattern doesn't change. However, the interpretation of the data changes. Consider this code:
#include <iostream>
using namespace std;
int main()
{
short i = -3;
unsigned short u;
cout << (u = i) << "\n";
}
// Output: 65533
In the preceding example, a signed short
, i
, is defined and initialized to a negative number. The expression (u = i)
causes i
to be converted to an unsigned short
before the assignment to u
.
Unsigned to signed
Objects of unsigned integral types can be converted to corresponding signed types. However, if the unsigned value is outside the representable range of the signed type, the result won't have the correct value, as demonstrated in the following example:
#include <iostream>
using namespace std;
int main()
{
short i;
unsigned short u = 65533;
cout << (i = u) << "\n";
}
//Output: -3
In the preceding example, u
is an unsigned short
integral object that must be converted to a signed quantity to evaluate the expression (i = u)
. Because its value can't be properly represented in a signed short
, the data is misinterpreted as shown.
Floating point conversions
An object of a floating type can be safely converted to a more precise floating type—that is, the conversion causes no loss of significance. For example, conversions from float
to double
or from double
to long double
are safe, and the value is unchanged.
An object of a floating type can also be converted to a less precise type, if it's in a range representable by that type. (See Floating Limits for the ranges of floating types.) If the original value isn't representable precisely, it can be converted to either the next higher or the next lower representable value. The result is undefined if no such value exists. Consider the following example:
cout << (float)1E300 << endl;
The maximum value representable by type float
is 3.402823466E38 which is a much smaller number than 1E300. Therefore, the number is converted to infinity, and the result is "inf".
Conversions between integral and floating point types
Certain expressions can cause objects of floating type to be converted to integral types, or vice versa. When an object of integral type is converted to a floating type, and the original value isn't representable exactly, the result is either the next higher or the next lower representable value.
When an object of floating type is converted to an integral type, the fractional part is truncated, or rounded toward zero. A number like 1.3 is converted to 1, and -1.3 is converted to -1. If the truncated value is higher than the highest representable value, or lower than the lowest representable value, the result is undefined.
Arithmetic conversions
Many binary operators (discussed in Expressions with binary operators) cause conversions of operands, and yield results the same way. The conversions these operators cause are called usual arithmetic conversions. Arithmetic conversions of operands that have different native types are done as shown in the following table. Typedef types behave according to their underlying native types.
Conditions for type conversion
Conditions Met | Conversion |
---|---|
Either operand is of type long double . |
Other operand is converted to type long double . |
Preceding condition not met and either operand is of type double . |
Other operand is converted to type double . |
Preceding conditions not met and either operand is of type float . |
Other operand is converted to type float . |
Preceding conditions not met (none of the operands are of floating types). | Operands get integral promotions as follows: - If either operand is of type unsigned long , the other operand is converted to type unsigned long .- If preceding condition not met, and if either operand is of type long and the other of type unsigned int , both operands are converted to type unsigned long .- If the preceding two conditions aren't met, and if either operand is of type long , the other operand is converted to type long .- If the preceding three conditions aren't met, and if either operand is of type unsigned int , the other operand is converted to type unsigned int .- If none of the preceding conditions are met, both operands are converted to type int . |
The following code illustrates the conversion rules described in the table:
double dVal;
float fVal;
int iVal;
unsigned long ulVal;
int main() {
// iVal converted to unsigned long
// result of multiplication converted to double
dVal = iVal * ulVal;
// ulVal converted to float
// result of addition converted to double
dVal = ulVal + fVal;
}
The first statement in the preceding example shows multiplication of two integral types, iVal
and ulVal
. The condition met is that neither operand is of floating type, and one operand is of type unsigned int
. So, the other operand, iVal
, is converted to type unsigned int
. The result is then assigned to dVal
. The condition met here is that one operand is of type double
, so the unsigned int
result of the multiplication is converted to type double
.
The second statement in the preceding example shows addition of a float
and an integral type: fVal
and ulVal
. The ulVal
variable is converted to type float
(third condition in the table). The result of the addition is converted to type double
(second condition in the table) and assigned to dVal
.
Pointer conversions
Pointers can be converted during assignment, initialization, comparison, and other expressions.
Pointer to classes
There are two cases in which a pointer to a class can be converted to a pointer to a base class.
The first case is when the specified base class is accessible and the conversion is unambiguous. For more information about ambiguous base-class references, see Multiple base classes.
Whether a base class is accessible depends on the kind of inheritance used in derivation. Consider the inheritance illustrated in the following figure:
The diagram shows base class A. Class B inherits from A via private protected public. Class C inherits from B via public B.
Inheritance graph illustrating base-class accessibility
The following table shows the base-class accessibility for the situation illustrated in the figure.
Type of Function | Derivation | Conversion fromB* to A* legal? |
---|---|---|
External (not class-scoped) function | Private | No |
Protected | No | |
Public | Yes | |
B member function (in B scope) | Private | Yes |
Protected | Yes | |
Public | Yes | |
C member function (in C scope) | Private | No |
Protected | Yes | |
Public | Yes |
The second case in which a pointer to a class can be converted to a pointer to a base class is when you use an explicit type conversion. For more information about explicit type conversions, see Explicit type conversion operator.
The result of such a conversion is a pointer to the subobject, the portion of the object that is completely described by the base class.
The following code defines two classes, A
and B
, where B
is derived from A
. (For more information on inheritance, see Derived Classes.) It then defines bObject
, an object of type B
, and two pointers (pA
and pB
) that point to the object.
// C2039 expected
class A
{
public:
int AComponent;
int AMemberFunc();
};
class B : public A
{
public:
int BComponent;
int BMemberFunc();
};
int main()
{
B bObject;
A *pA = &bObject;
B *pB = &bObject;
pA->AMemberFunc(); // OK in class A
pB->AMemberFunc(); // OK: inherited from class A
pA->BMemberFunc(); // Error: not in class A
}
The pointer pA
is of type A *
, which can be interpreted as meaning "pointer to an object of type A
." Members of bObject
(such as BComponent
and BMemberFunc
) are unique to type B
and are therefore inaccessible through pA
. The pA
pointer allows access only to those characteristics (member functions and data) of the object that are defined in class A
.
Pointer to function
A pointer to a function can be converted to type void *
, if type void *
is large enough to hold that pointer.
Pointer to void
Pointers to type void
can be converted to pointers to any other type, but only with an explicit type cast (unlike in C). A pointer to any type can be converted implicitly to a pointer to type void
. A pointer to an incomplete object of a type can be converted to a pointer to void
(implicitly) and back (explicitly). The result of such a conversion is equal to the value of the original pointer. An object is considered incomplete if it's declared, but there's insufficient information available to determine its size or base class.
A pointer to any object that isn't const
or volatile
can be implicitly converted to a pointer of type void *
.
const and volatile pointers
C++ doesn't supply a standard conversion from a const
or volatile
type to a type that's not const
or volatile
. However, any sort of conversion can be specified using explicit type casts (including conversions that are unsafe).
Note
C++ pointers to members, except pointers to static members, are different from normal pointers and don't have the same standard conversions. Pointers to static members are normal pointers and have the same conversions as normal pointers.
null pointer conversions
An integral constant expression that evaluates to zero, or such an expression cast to a pointer type, is converted to a pointer called the null pointer. This pointer always compares unequal to a pointer to any valid object or function. An exception is pointers to based objects, which can have the same offset and still point to different objects.
In C++11, the nullptr type should be preferred to the C-style null pointer.
Pointer expression conversions
Any expression with an array type can be converted to a pointer of the same type. The result of the conversion is a pointer to the first array element. The following example demonstrates such a conversion:
char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].
An expression that results in a function returning a particular type is converted to a pointer to a function returning that type, except when:
The expression is used as an operand to the address-of operator (&).
The expression is used as an operand to the function-call operator.
Reference conversions
A reference to a class can be converted to a reference to a base class in these cases:
The specified base class is accessible.
The conversion is unambiguous. (For more information about ambiguous base-class references, see Multiple base classes.)
The result of the conversion is a pointer to the subobject that represents the base class.
Pointer to member
Pointers to class members can be converted during assignment, initialization, comparison, and other expressions. This section describes the following pointer-to-member conversions:
Pointer to base class member
A pointer to a member of a base class can be converted to a pointer to a member of a class derived from it, when the following conditions are met:
The inverse conversion, from pointer to derived class to base-class pointer, is accessible.
The derived class doesn't inherit virtually from the base class.
When the left operand is a pointer to member, the right operand must be of pointer-to-member type or be a constant expression that evaluates to 0. This assignment is valid only in the following cases:
The right operand is a pointer to a member of the same class as the left operand.
The left operand is a pointer to a member of a class derived publicly and unambiguously from the class of the right operand.
null pointer to member conversions
An integral constant expression that evaluates to zero is converted to a null pointer. This pointer always compares unequal to a pointer to any valid object or function. An exception is pointers to based objects, which can have the same offset and still point to different objects.
The following code illustrates the definition of a pointer to member i
in class A
. The pointer, pai
, is initialized to 0, which is the null pointer.
class A
{
public:
int i;
};
int A::*pai = 0;
int main()
{
}