C++ Basics [07]: Argument Type Conversions

6 minute read

Published:

When there is no exact match for overloading functions, type conversions might be necessary. In order to determine the best match, the compiler ranks the conversions that could be used to convert each argument to the type of its corresponding parameter. These type conversions are summarized in this post.


Implicit Conversions

Arithmetic Conversions

  • In most expressions, values of integral types smaller than int are first promoted to an appropriate larger integral type.
  • In conditions, nonbool expressions are converted to bool.
  • In initializations, the initializer is converted to the type of the variable; in assignments, the right-hand operand is converted to the type of the left-hand.
  • In arithmetic and relational expressions with operands of mixed types, the types are converted to a common type.
    • If both (possibly promoted) operands have the same signedness, then the operand with the smaller type is converted to the larger type.
    • When the signedness differs and the type of the unsigned operand is the same as or larger than that of the signed operand, the signed operand is converted to unsigned.
    • When the signed operand has a larger type than the unsigned operand, the result is machine dependent.
      • If all values in the unsigned type fit in the larger type, then the unsigned operand is converted to the signed type.
      • If the values don’t fit, then the signed operand is converted to the unsigned type.
bool flag; 
char cval;
short sval; 
unsigned short usval;
int ival; 
unsigned int uival;
long lval; 
unsigned long ulval;
float fval; 
double dval;

ival = dval;        // dval converted (by truncation) to int
flag = dval;        // if dval is 0, then flag is false, otherwise true
3.14159L + 'a';     // 'a' promoted to int, then that int converted to long double
dval + ival;        // ival converted to double
dval + fval;        // fval converted to double
cval + fval;        // cval promoted to int, then that int converted to float
sval + cval;        // sval and cval promoted to int
cval + lval;        // cval converted to long
ival + ulval;       // ival converted to unsigned long
usval + ival;       // promotion depends on the size of unsigned short and int
uival + lval;       // conversion depends on the size of unsigned int and long
  • A constant integral value of 0 and the literal nullptr can be converted to any pointer type.
  • A pointer to any nonconst type can be converted to void*.
  • A pointer to any type can be converted to a const void*.
  • An array is automatically converted to a pointer to the first element in that array.
    int arr[10];
    int* ptr = arr;     // convert arr to a pointer to the first element 
    
  • A function is automatically converted to a pointer when we use the name of a function as a value.
    int Sum(int a, int b){
      return a + b;
    }
    int (*pf)(int, int);
    pf = Sum;
    pf = ∑  // the same 
    
  • A zero pointer is converted to false in conditions; otherwise, it is true.
    if (ptr) /* . . . */      //true if the pointer cp is not zero
    while (*ptr) /* . . . */  // true if *cp is not the null character
    
  • A pointer or reference to T can be converted to a pointer or reference to const T; the reverse doesn’t.

Explicit Conversions

Old style casts have the form:

type (expr); // function-style cast notation
(type) expr; // C-language-style cast notation

Now, it is recommended to use the named casts. A named cast has the form:

cast-name(expression);

stataic_cast

  • A static_cast is often useful when a larger arithmetic type is assigned to a smaller type.
  • A static_cast is also useful to perform a conversion that the compiler will not generate automatically. For example, we can use a static_cast to retrieve a pointer value that was stored in a void* pointer.
    double i = 1;
    void* ptr1 = &i;
    double* ptr2 = static_cast<double*>(ptr1);
    

dynamic_cast

In process … …

const_cast

A const_cast changes only a low-level const in its operand, and it doesn’t change the type of a variable. If the object was originally not a const, using a cast to obtain write access is legal. However, using a const_cast in order to write to a const object is undefined.

const char *cp;
char *ptr1 = const_cast<char*>(cp);     // ok: but writing through ptr1 is undefined
char *ptr2 = static_cast<char*>(cp);    // error
static_cast<string>(cp);                // ok: converts string literal to string
const_cast<string>(cp);                 // error: const_cast only changes constness

A const_cast is most useful in the context of overloaded functions.

reinterprete_cast

In process … …


Argument Type Conversions

During functions matching, conversions are ranked as follows:

  1. An exact match. An exact match happens when:
    • The argument and parameter types are identical.
    • The argument is converted from an array or function type to the corresponding pointer type.
    • A top-level const is added to or discarded from the argument.
  2. Match through a const conversion.
  3. Match through a promotion.
  4. Match through an arithmetic or pointer conversion.
  5. Match through a class-type conversion.

It is to be noted that all the arithmetic conversions are treated as equivalent to each other. The conversion from int to unsigned int, for example, does not take precedence over the conversion from int to double. Here is an example.

void manip(long);
void manip(float);
manip(3.14);        // error: ambiguous call

For const arguments, although a const parameter can accept both const and nonconst arguments, when a nonconst argument is passed to the overloaded functions, the compiler will still prefer the nonconst versions (e.g., pointer to const, reference to const). Here is an example.

Record lookup(Account&);        // function that takes a reference to Account
Record lookup(const Account&);  // new function that takes a const reference
const Account a;
Account b;
lookup(a);                      // calls lookup(const Account&)
lookup(b);                      // calls lookup(Account&)

Table of Contents

Comments