Storing int type inside char type

I was reading Bjarne Stroustrup's book on page 72 that spoke about types and declarations (Chapter 4) in sixth paragraph in second line that said: if you need to use more than one type of char or if you store integers in char variables (this part caught my attention), well, I didn't understand why store a int variable in a char variable, but when I written the code and created a char variable with value 5 (char ch = 5) and a other int variable with value 10 (int i = 10) and print the sum of variables It added without any error, but How can It be done such operation being that the variables are types differents?

1
2
3
4
5
6
7
8
9
10
  #include <iostream>

using namespace std;

int main(){
        int i = 10;
        char ch = 5;
        cout << ch+i << endl;

}
In C++, char is an integer type.

When we perform arithmetic operations using mixed arithmetic types, as in ch+i, conversions called usual arithmetic conversions are performed to yield a common type.

In this case, a conversion from char to int (a promotion) is performed on ch to get a temporary int (which has the same value, 5 in this case) and then the two int values are added to yield a result of type int.

As if:
1
2
3
4
5
6
7
8
9
10
int main(){

        int i = 10;
        char ch = 5;

        const int temp1 = ch ;
        const int temp2 =  temp1 + i ;

        cout << temp2 << endl;
}
chars are integers in most (all??) languages. The operating system has a lookup table of font images to display when printing at text on the screen. The rest of the time, when not showing pixels on the screen, its just an integer. It is a larger integer for Unicode or other schemes.

My personal approach in professional code is to use the
int8_t for integers and
char for "characters" or text related processing.

Ill admit to just using char for disposable code though.
It's not just chars that are ints.

Though bools are a different type than int, they can be used wherever an int is.

For instance, a template can take type arguments and int non-type arguments.

Eg:

1
2
3
4
5
template<typename  T, int N>
void f()
{
 /// ...
}


Here, you can instantiate the template with a bool in place of the N, since an implicit type conversion takes place.

Eg:
 
f<A, b>();   /// A is a class, b is a bool 


Also, enums and enum classes are implemented, by default, as ints.
Therefore an enum value can be passed where an int is expected. However, an enum class can't since it isn't interchangeable with an int.

Finally a class can define a conversion operator to an int. If so, it can also be used where an int is expected.

use std::is_integral<T> to check if type T is an integral type. Note however that the fact T can be converted to int does not necessarily mean that T is_integral, e.g. type A in the program below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <type_traits>

struct A
{
    int m_x;

    operator int()
   {
      return m_x;
   }
};
enum class days{Sun, Mon, Tue};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_integral <char>::value << '\n';   // true
    std::cout << std::is_integral <size_t>::value << '\n';   // true
    std::cout << std::is_integral <bool>::value << '\n';   // true
    std::cout << std::is_integral <short>::value << '\n';   // true
    std::cout << std::is_integral <days>::value << '\n';   // false
    std::cout << std::is_integral <A>::value << '\n';   // false

    A a{10};
    std::cout << static_cast<int>(a) << "\n"; //10
}


Continuing with gunner's example ...

In fact you don't even need to do an explicit static_cast of A to an int. After all, in general, explicit casts are frowned upon, as possible design problems.

Using a template, you can do an implicit conversion. (I had mentioned this in my earlier post.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct A
{
    int m_x;

    operator int()
   {
      return m_x;
   }
};


template<typename T>
struct Int1
{
    int t;

    Int1(int tt) :
        t {tt}
    {
    }
};

A a5 {5};

/// ctor expects an int.
/// An implicit type conversion is performed since
/// type A defines a conversion operator to int.
Int1<A> i1a {a5};


int main()
{
    cout << "i1a.t = " << i1a.t << endl;	/// 5

    return 0;
}



However, look at the following template class (which expects an int, rather than any type) and an attempted instantiation with A:

1
2
3
4
5
6
7
8
9
10
11
12
template<int T>
struct Int2
{
    int t;

    Int2(int tt) :
        t {tt}
    {
    }
};

Int2<A> i2a {a5};	/// error 


You will get a compilation error:


|error: type/value mismatch at argument 1 in template parameter list for 'template<int T> struct Int2'|
|error:   expected a constant of type 'int', got 'A'|


In this situation, an implicit type conversion from the type to int doesn't happen, despite the type's int conversion operator.
(Why doesn't this happen?)
Last edited on
Why doesn't this happen?

because Int2 is parameterized on int T, so Int2<1> would be one data-type, Int2<2> another and so on, the template instantiation can only be over ints.
Also note in your example the template parameter is unused, one possible way to use it might be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
template<int T>
struct Int2
{
    int t;

    Int2() : t {T}  {}
};
template <int T> std::ostream& operator << (std::ostream& os, const Int2<T>& i)
{
    os << i.t << " \n"; return os;
}

int main()
{
    Int2<3> i2a {};
    std::cout << i2a; // prints 3
}

Topic archived. No new replies allowed.