C++

C++ Standard Conversions

C++ Standard Conversions
There are two entity types in C++, the fundamental types and the compound types. The fundamental types are the scalar types. The compound types are the rest of the entity types. Conversion can take place from one entity type to another appropriate type. Consider the following program:

#include
#include
using namespace std;
int main()

int rt1 = sqrt(5);
int rt2 = sqrt(8);
cout<return 0;

The output is 2, 2, meaning that the program has returned the square root of 5 as 2 and the square root of 8 also as 2. So, the first two statements in the main() function have floored the answers of the square root of 5 and the square root of 8. This article does not discuss flooring or ceiling in C++. Rather, this article discusses the conversion of one C++ type to another appropriate C++ type; indicating any approximation in value made, loss of precision, or constraint added or removed. Basic knowledge of C++ is a prerequisite to understand this article.

Article Content

  • Integral Conversions
  • Floating-Point Conversions
  • Floating-Integral Conversions
  • Integer Conversion Ranking
  • Integral Promotions
  • Usual Arithmetic Conversions
  • Floating-Point Promotion
  • Pointer Conversions
  • Function to Pointer Conversions
  • Boolean Conversions
  • Lvalue, prvalue, and xvalue
  • Xvalue
  • Lvalue-to-rvalue Conversions
  • Array-to-Pointer Conversions
  • Function-to-Pointer Conversions
  • Temporary Materialization Conversions
  • Qualification Conversions
  • Conclusion

Integral Conversions

Integral conversions are integer conversions. Unsigned integers include “unsigned char,” “unsigned short int,” “unsigned int,” “unsigned long int,” and “unsigned long long int.” The corresponding signed integers include “signed char,” “short int,” “int,” “long int,” and “long long int.” Each int type should be held in as many bytes as its predecessor. For most systems, one entity type can be converted to a corresponding type without any issue. The problem occurs when converting from a larger range type to a smaller range type, or when converting a signed number to a corresponding unsigned number.

Each compiler has a maximum value that it can take for the short int. If a number higher than that maximum, meant for an int, is assigned to the short int, the compiler will follow some algorithm and return a number within the range of the short int. If the programmer is lucky, the compiler will warn of trouble with using inappropriate conversion. The same explanation applies to conversions of other int types.

The user should consult the documentation of the compiler to determine the limiting values for each entity type.

If a negative signed short int number is to be converted into an unsigned short int number, the compiler will follow some algorithm and return a positive number within the range of the unsigned short int. This kind of conversion should be avoided. The same explanation applies to conversions of other int types.

Any integer number, except 0, can be converted to Boolean true. 0 is converted to Boolean false. The following code illustrates this:

int a = -27647;
float b = 2.5;
int c = 0;
bool a1 = a;
bool b1 = b;
bool c1 = c;
cout<cout<cout<The output is:

1 for true
1 for true
0 for false

Floating-Point Conversions

Floating-point types include “float,” “double,” and “long double.” Floating-point types are not grouped into signed and unsigned, like integers. Each type can have a signed or unsigned number. A floating-point type should have at least the same precision as its predecessor. That is, “long double” should have equal or greater precision to “double,” and “double” should have equal or greater precision to “float.”

Remember that the range of a floating-point type is not continuous; rather, it is in small steps. The greater the precision of the type, the smaller the steps, and the greater the number of bytes to store the number. So, when a floating-point number is converted from a lower precision type to a higher precision type, the programmer must accept a false increase in precision and a possible increase in the number of bytes for number-storage. When a floating-point number is converted from a higher precision type to a lower precision type, the programmer must accept a loss in precision. If the number of bytes for number-storage must be reduced, then the compiler will follow some algorithm and return a number as a substitute (which is probably not what the programmer wants). Also, bear in mind out-of-range problems.

Floating-Integral Conversions

A floating-point number is converted to an integer by truncating off the fractional part. The following code illustrates this:

float f = 56.953;
int i = f;
cout<The output is 56. The ranges for the float and integer must be compatible.

When an integer is converted into a float, the value displayed as a float is the same as was typed in as an integer. However, the float equivalent might be the exact value or have a slight fractional difference that is not displayed. The reason for the fractional difference is that floating-point numbers are represented in the computer in small fractional steps, and so representing the integer exactly would be a coincidence. So, though the integer displayed as a float is the same as was typed, the display may be an approximation of what is stored.

Integer Conversion Ranking

Any integer type has a rank that has been given to it. This ranking aids in conversion. The ranking is relative; the ranks are not at fixed levels. Except for char and signed char, no two signed integers have the same rank (assuming that char is signed). Unsigned integer types have the same ranking as their corresponding signed integer types. The ranking is as follows:

  • Assuming that char is signed, then char and signed char have the same rank.
  • The rank of a signed integer type is greater than the rank of a signed integer type of a smaller number of storage bytes. So, the rank of signed long long int is greater than the rank of signed long int, which is greater than the rank of signed int, which is greater than the rank of signed short int, which is greater than the rank of signed char.
  • The rank of any unsigned integer type equals the rank of the corresponding signed integer type.
  • The rank of unsigned char equals the rank of signed char.
  • bool has the least rank; its rank is less than that of signed char.
  • char16_t has the same rank as the short int. char32_t has the same rank as the int. For the g++ compiler, wchar_t has the same rank as the int.

Integral Promotions

Integral Promotions is Integer Promotions. There is no reason why an integer of fewer bytes cannot be represented by an integer of greater bytes. Integer Promotions deals with all that follows:

  • A signed short int (two bytes) can be converted to a signed int (four bytes). An unsigned short int (two bytes) can be converted to an unsigned int (four bytes). Note: converting a short int to a long int or a long long int leads to a waste of storage (object location) bytes and a waste of memory. Bool, char16_t, char32_t, and wchar_t are exempted from this promotion (with the g++ compiler, char32_t and wchar_t have the same number of bytes).
  • With the g++ compiler, a char16_t type can be converted to a signed int type or an unsigned int type; a char32_t type can be converted to a signed int type or an unsigned int type; and a wchar_t type can be converted to a signed or unsigned int type.
  • A bool type can be converted to an int type. In this case, true becomes 1 (four bytes) and false becomes 0 (four bytes). Int may be signed or signed.
  • Integer promotion also exists for unscoped enumeration type - see later.

Usual Arithmetic Conversions

Consider the following code:

float f = 2.5;
int i = f;
cout<The code compiles without indicating any warning or error, giving the output of 2, which is probably not what was expected. = is a binary operator because it takes a left and right operand. Consider the following code:

int i1 = 7;
int i2 = 2;
float flt = i1 / i2;
cout<The output is 3, but this is wrong; it was supposed to be 3.5. The division operator, /, is also a binary operator.

C++ has usual arithmetic conversions that the programmer must know to avoid errors in coding. The usual arithmetic conversions on binary operators are as follows:

  • If either operand is of the type “long double,” then the other will be converted to long double.
  • Else, if either operand is double, the other will be converted to double.
  • Else, if either operand is float, the other will be converted to float. In the above code, the result of i1/i2 is officially 2; that is why flt is 2. The result of the binary, /, is applied as the right operand to the binary operator, =. So, the final value of 2 is a float (not an int).

ELSE, INTEGER PROMOTION WOULD TAKE PLACE AS FOLLOWS:

  • If both operands are of the same type, then no further conversion takes place.
  • Else, if both operands are signed integer types or both are unsigned integer types, then the operand of the type with the lower integer rank will be converted to the type of the operand with the higher rank.
  • Else, if one operand is signed and the other is unsigned, and if the unsigned operand type is greater than or equal to the rank of the signed operand type, and if the value of the signed operand is greater than or equal to zero, then the signed operand will be converted to the unsigned operand type (with range taken into consideration). If the signed operand is negative, then the compiler will follow an algorithm and return a number that may not be acceptable to the programmer.
  • Else, if one operand is a signed integer type and the other is an unsigned integer type, and if all possible values of the type of the operand with the unsigned integer type can be represented by the signed integer type, then the unsigned integer type will be converted to the type of the operand of the signed integer type.
  • Else, the two operands (a char and a bool, for example) would be converted to the unsigned integer type.

Floating-Point Promotion

Floating-point types include “float,” “double,” and “long double.” A floating-point type should have at least the same precision as its predecessor. Floating-point promotion allows for conversion from float to double or from double to long double.

Pointer Conversions

A pointer of one object type cannot be assigned to a pointer of a different object type. The following code will not compile:

int id = 6;
int* intPtr = &id;
float idf = 2.5;
float* floatPtr = &idf;
intPtr = floatPtr; // error here

A null pointer is a pointer whose address value is zero. A null pointer of one object type cannot be assigned to a null pointer of a different object type. The following code will not compile:

int id = 6;
int* intPtr = &id;
intPtr = 0;
float idf = 2.5;
float* floatPtr = &idf;
floatPtr = 0;
intPtr = floatPtr; // error here

A null pointer const of one object type cannot be assigned to a null pointer const of a different object type. The following code will not compile:

int id = 6;
int* intPtr = &id;
int* const intPC = 0;
float idf = 2.5;
float* floatPtr = &idf;
float* const floatPC = 0;
intPC = floatPC; // error here

A null pointer can be given a different address value for its type. The following code illustrates this:

float idf = 2.5;
float* floatPtr = 0;
floatPtr = &idf;
cout<<*floatPtr<<'\n';

The output is 2.5.

As expected, a null pointer constant cannot be assigned any address value of its type. The following code will not compile:

float idf = 2.5;
float* const floatPC = 0;
floatPC = &idf; //error here

However, a null pointer constant can be assigned to an ordinary pointer, but of the same type (this is to be expected). The following code illustrates this:

float idf = 2.5;
float* const floatPC = 0;
float* floatPter = &idf;
floatPter = floatPC; //OK
cout << floatPter << '\n';

The output is 0.

Two null pointer values of the same type compare (==) equal.

A pointer to an object type can be assigned to a pointer to void. The following code illustrates this:

float idf = 2.5;
float* floatPtr = &idf;
void* vd;
vd = floatPtr;

The code compiles without a warning or error message.

Function to Pointer Conversions

A pointer to a function that would not throw an exception can be assigned to a pointer to function. The following code illustrates this:

#include
using namespace std;
void fn1() noexcept

cout << "with noexcept" << '\n';

void fn2()

//statements

void (*func1)() noexcept;
void (*func2)();
int main()

func1 = &fn1;
func2 = &fn2;
func2 = &fn1;
func2();
return 0;

The output is with noexcept.

Boolean Conversions

In C++, entities that can result in false include “zero,” “null pointer,” and “null member pointer.” All other entities result in true. The following code illustrates this:

bool a = 0.0; cout << a <<'\n';
float* floatPtr = 0;
bool b = floatPtr; cout << b <<'\n';
bool c = -2.5; cout << c <<'\n';
bool d = +2.5; cout << d <<'\n';

The output is:

0 //for false
0 //for false
1 //for true
1 //for true

Lvalue, prvalue and xvalue

Consider the following code:

int id = 35;
int& id1 = id;
cout << id1 << '\n';

The output is 35. In the code, id and id1 are lvalues because they identify a location (object) in memory. The output 35 is a prvalue. Any literal, except a string literal, is a prvalue. Other prvalues are not so obvious, as in the examples that follow. Consider the following code:

int id = 62;
int* ptr = &id;
int* pter;

Ptr is an lvalue because it identifies a location (object) in memory. On the other hand, pter is not an lvalue. Pter is a pointer, but it does not identify any location in memory (it is not pointing to any object). So, pter is a prvalue.

Consider the following code:

void fn()

//statements

void (*func)() = &fn;
float (*functn)();

Fn() and (*func)() are lvalue expressions because they identify an entity (function) in memory. On the other hand, (*functn)() is not an lvalue expression. (*functn)() is a pointer to a function, but it does not identify any entity in memory (it is not pointing to any function in memory). So, (*functn)() is a prvalue expression.

Now, consider the following code:

struct S

int n;
;
S obj;

S is a class and obj is an object instantiated from the class. Obj identifies an object in memory. A class is a generalized unit. So, S does not really identify any object in memory. S is said to be an unnamed object. S is also a prvalue expression.

The focus of this article is on prvalues. Prvalue means pure rvalue.

Xvalue

Xvalue stands for Expiring Value. Temporary values are expiring values. An lvalue can become an xvalue. A prvalue can also become an xvalue. The focus of this article is on prvalues. An xvalue is an lvalue or an unnamed rvalue reference whose storage can be reused (usually because it is near the end of its lifetime). Consider the following code that works:

struct S

int n;
;
int q = S().n;

The expression “int q = S().n;” copies whatever value n holds to q. S() is just a means; it is not a regularly used expression. S() is a prvalue whose use has converted it to an xvalue.

Lvalue-to-rvalue Conversions

Consider the following statement:

int ii = 70;

70 is a prvalue (rvalue) and ii is an lvalue. Now, consider the following code:

int ii = 70;
int tt = ii;

In the second statement, ii is in the situation of a prvalue, so ii becomes a prvalue there. In other words, the compiler converts ii to a prvalue implicitly. That is, when an lvalue is used in a situation in which the implementation expects a prvalue, the implementation converts the lvalue to a prvalue.

Array-to-Pointer Conversions

Consider the following code that works:

char* p;
char q[] = 'a', 'b', 'c';
p = &q[0];
++p;
cout<<*p<<'\n';

The output is b. The first statement is an expression and is a pointer to a character. But to which character is the statement pointing? - No character. So, it is a prvalue and not an lvalue. The second statement is an array in which q[] is an lvalue expression. The third statement turns the prvalue, p, into an lvalue expression, which points to the first element of the array.

Function-to-Pointer Conversions

Consider the following program:

#include
using namespace std;
void (*func)();
void fn()

//statements

int main()

func = &fn;
return 0;

The expression “void (*func)();” is a pointer to a function. But to which function is the expression pointing? - No function. So, it is a prvalue and not an lvalue. Fn() is a function definition, where fn is an lvalue expression. In main(), “func = &fn;” turns the prvalue, func, into an lvalue expression that points to the function, fn().

Temporary Materialization Conversions

In C++, a prvalue can be converted to an xvalue of the same type. The following code illustrates this:

struct S

int n;
;
int q = S().n;

Here, the prvalue, S(), has been converted to an xvalue. As an xvalue, it would not last long - see more explanation above.

Qualification Conversions

A cv-qualified type is a type qualified by the reserved word, “const,” and/or the reserved word, “volatile.”

Cv-qualification is also ranked. No cv-qualification is less than “const” qualification, which is less than “const volatile” qualification. No cv-qualification is less than “volatile” qualification, which is less than “const volatile” qualification. So, there are two streams of qualification ranking. One type can be more cv-qualified than another.

A lower prvalue cv-qualified type can be converted to a more cv-qualified prvalue type. Both types should be pointer-to-cv.

Conclusion

C++ entities can be converted from one type to a related type implicitly or explicitly. However, the programmer must understand what can be converted and what cannot be converted, and into what form. Conversion can take place in the following domains: Integral Conversions, Floating-Point Conversions, Floating-Integral Conversions, Usual Arithmetic Conversions, Pointer Conversions, Function to Pointer Conversions, Boolean Conversions, Lvalue-to-rvalue Conversions, Array-to-Pointer Conversions, Function-to-Pointer Conversions, Temporary Materialization Conversions, and Qualification Conversions.

Gry How to Install and Play Doom on Linux
How to Install and Play Doom on Linux
Introduction to Doom The Doom Series originated in the 90s after the release of the original Doom. It was an instant hit and from that time onwards th...
Gry Vulkan for Linux Users
Vulkan for Linux Users
With each new generation of graphics cards, we see game developers push the limits of graphical fidelity and come one step closer to photorealism. But...
Gry OpenTTD vs Simutrans
OpenTTD vs Simutrans
Creating your own transport simulation can be fun, relaxing and extremely enticing. That's why you need to make sure that you try out as many games as...