Category: Type System & Data Representation

https://zain.sweetdishy.com/wp-content/uploads/2026/02/Type-System-Data-Representation-.png

  • C++ Date and Time

    The C++ standard library does not provide a proper date type. C++ inherits the structs and functions for date and time manipulation from C. To access date and time related functions and structures, you would need to include <ctime> header file in your C++ program.

    There are four time-related types: clock_t, time_t, size_t, and tm. The types – clock_t, size_t and time_t are capable of representing the system time and date as some sort of integer.

    The structure type tm holds the date and time in the form of a C structure having the following elements −

    structtm{int tm_sec;// seconds of minutes from 0 to 61int tm_min;// minutes of hour from 0 to 59int tm_hour;// hours of day from 0 to 24int tm_mday;// day of month from 1 to 31int tm_mon;// month of year from 0 to 11int tm_year;// year since 1900int tm_wday;// days since sundayint tm_yday;// days since January 1stint tm_isdst;// hours of daylight savings time}

    Following are the important functions, which we use while working with date and time in C or C++. All these functions are part of standard C and C++ library and you can check their detail using reference to C++ standard library given below.

    Sr.NoFunction & Purpose
    1time_t time(time_t *time);This returns the current calendar time of the system in number of seconds elapsed since January 1, 1970. If the system has no time, .1 is returned.
    2char *ctime(const time_t *time);This returns a pointer to a string of the form day month year hours:minutes:seconds year\n\0.
    3struct tm *localtime(const time_t *time);This returns a pointer to the tm structure representing local time.
    4clock_t clock(void);This returns a value that approximates the amount of time the calling program has been running. A value of .1 is returned if the time is not available.
    5char * asctime ( const struct tm * time );This returns a pointer to a string that contains the information stored in the structure pointed to by time converted into the form: day month date hours:minutes:seconds year\n\0
    6struct tm *gmtime(const time_t *time);This returns a pointer to the time in the form of a tm structure. The time is represented in Coordinated Universal Time (UTC), which is essentially Greenwich Mean Time (GMT).
    7time_t mktime(struct tm *time);This returns the calendar-time equivalent of the time found in the structure pointed to by time.
    8double difftime ( time_t time2, time_t time1 );This function calculates the difference in seconds between time1 and time2.
    9size_t strftime();This function can be used to format date and time in a specific format.

    Current Date and Time

    Suppose you want to retrieve the current system date and time, either as a local time or as a Coordinated Universal Time (UTC).

    Example

    Following is the example to achieve the same −

    #include <iostream>#include <ctime>usingnamespace std;intmain(){// current date/time based on current system
       time_t now =time(0);// convert now to string formchar* dt =ctime(&now);
    
       cout <<"The local date and time is: "<< dt << endl;// convert now to tm struct for UTC
       tm *gmtm =gmtime(&now);
       dt =asctime(gmtm);
       cout <<"The UTC date and time is:"<< dt << endl;}

    When the above code is compiled and executed, it produces the following result −

    The local date and time is: Sat Jan  8 20:07:41 2011
    
    The UTC date and time is:Sun Jan  9 03:07:41 2011
    

    Format Time using struct tm

    The tm structure is very important while working with date and time in either C or C++. This structure holds the date and time in the form of a C structure as mentioned above. Most of the time related functions makes use of tm structure. Following is an example which makes use of various date and time related functions and tm structure −

    While using structure in this chapter, I’m making an assumption that you have basic understanding on C structure and how to access structure members using arrow -> operator.

    Example

    #include <iostream>#include <ctime>usingnamespace std;intmain(){// current date/time based on current system
       time_t now =time(0);
    
       cout <<"Number of sec since January 1,1970 is:: "<< now << endl;
    
       tm *ltm =localtime(&now);// print various components of tm structure.
       cout <<"Year:"<<1900+ ltm->tm_year<<endl;
       cout <<"Month: "<<1+ ltm->tm_mon<< endl;
       cout <<"Day: "<< ltm->tm_mday << endl;
       cout <<"Time: "<<5+ltm->tm_hour <<":";
       cout <<30+ltm->tm_min <<":";
       cout << ltm->tm_sec << endl;}

    When the above code is compiled and executed, it produces the following result −

    Number of sec since January 1,1970 is:: 1588485717
    Year:2020
    Month: 5
    Day: 3
    Time: 11:31:57
  • C++ References

    A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable.

    References vs Pointers

    References are often confused with pointers but three major differences between references and pointers are −

    • You cannot have NULL references. You must always be able to assume that a reference is connected to a legitimate piece of storage.
    • Once a reference is initialized to an object, it cannot be changed to refer to another object. Pointers can be pointed to another object at any time.
    • A reference must be initialized when it is created. Pointers can be initialized at any time.

    Creating References in C++

    Think of a variable name as a label attached to the variable’s location in memory. You can then think of a reference as a second label attached to that memory location. Therefore, you can access the contents of the variable through either the original variable name or the reference. For example, suppose we have the following example −

    int i =17;

    We can declare reference variables for i as follows.

    int& r = i;

    Read the & in these declarations as reference. Thus, read the first declaration as “r is an integer reference initialized to i” and read the second declaration as “s is a double reference initialized to d.”. Following example makes use of references on int and double −

    #include <iostream>usingnamespace std;intmain(){// declare simple variablesint    i;double d;// declare reference variablesint&    r = i;double& s = d;
       
       i =5;
       cout <<"Value of i : "<< i << endl;
       cout <<"Value of i reference : "<< r  << endl;
     
       d =11.7;
       cout <<"Value of d : "<< d << endl;
       cout <<"Value of d reference : "<< s  << endl;return0;}

    When the above code is compiled together and executed, it produces the following result −

    Value of i : 5
    Value of i reference : 5
    Value of d : 11.7
    Value of d reference : 11.7
    

    References are usually used for function argument lists and function return values. So following are two important subjects related to C++ references which should be clear to a C++ programmer −

    Sr.NoConcept & Description
    1References as ParametersC++ supports passing references as function parameter more safely than parameters.
    2Reference as Return ValueYou can return reference from a C++ function like any other data type.
  • enum Class in C++

    An enumeration, or enum in C++, is a user-defined data type formed by a set of named integral constants. However, enum types from tradition have the disadvantage of potential name clashes and the absence of type safety.

    To solve this problem, C++ introduced the concept of enum class, which is also referred to as scoped enumerations.

    Declaring an enum Class

    This is the following syntax of declaring an enum class, which is done by using scoped enumeration (enum class).

    enumclassEnumName{
        Value1,
        Value2,
        Value3,// ... other enumerator values.};

    Here, EnumName is the name of the enumeration class.

    Value1, Value2.. is the named value(constants) inside the enum class.

    Accessing Values from an enum Class

    You can access the values of an enum class by using the Scope Resolution Operator (::) with the enum class name. Consider the following syntax:

    EnumName::Value

    Here, EnumName is the name of the enum class. Value is one of the enumerators defined in the class.

    Example of Enum Class

    Here is the following example for the enum class in C++.

    #include <iostream>usingnamespace std;// Declare an enum class for days of the weekenumclassDay{
        Monday,// 0
        Tuesday,// 1
        Wednesday,// 2
        Thursday,// 3
        Friday,// 4
        Saturday,// 5
        Sunday      // 6};intmain(){
        Day today = Day::Friday;if(today == Day::Friday){
            cout <<"Today is Friday!"<< endl;}return0;}

    Output

    Today is Friday!

    Explanation

    • First, we declared an enum class named Day with their values.
    • And then set the variable today to Day::Friday, and then we compared it to Day::Friday to print a message.

    Underlying Type of an Enum Class

    The enum class uses an integer as its default underlying type but many more underlying types are possible: unsigned int, char, uint8_t, int16_t, etc.

    Default Underlying Type of enum class

    When you declare the enum class without specifying any underlying type, then by default it is set to int. This means that each enumerator’s value will be stored as an int (4 bytes).

    As we already see in the above following example.

    Specifying a Custom Underlying Type for an enum class

    Here, you can explicitly specify the underlying type of an enum class by adding a colon (:) after the enum class name. This will allow for control of the storage size and range of values.

    Syntax

    Here is the following syntax for it.

    enumclassEnumName:UnderlyingType{
        Value1,
        Value2,
        Value3
    };

    Here, UnderlyingType could be any integral type (like unsigned int, char, uint8_t, etc.).

    Example of Underlying Type of an Enum Class

    Here is the following example using unsigned int as Underlying Type.

    #include <iostream>usingnamespace std;enumclassStatus:unsigned int{
        Ok =0,// 0
        Error =1,// 1
        Warning =2// 2};intmain(){
        Status s = Status::Error;// Print the integer value of the enum
        cout <<"The integer value of Error is: "<<static_cast<unsigned int>(s)<< endl;return0;}

    Output

    The integer value of Error is: 1
  • C++ – Enumeration (Enum)

    In C++, an enumeration (or, enum) is a user-defined data type, where it consists of a set of named integral constants.

    Creating (Declaring) Enumeration Type

    To create an enumeration (enum), use the enum keyword followed by the name of the enumeration and a list of the named values further enclosed within curly braces.

    Syntax

    The following is the syntax to create an enumeration −

    enumenumName{
       Enumerator_value1,
       Enumerator_value2,
       Enumerator_value3,//  So on};

    Here,

    • enum is the keyword to declare enumeration type.
    • enumName is the name of the enumeration.
    • Enumerator_Value1, Enumerator_Value1, Enumerator_Value1, and so on are the integral constants.

    Accessing Enumeration

    You can access enumeration (enum) by declaring it with a variable name inside the int main() body and calling it as per requirement.

    Syntax

    Here’s a syntax for accessing or calling an enum:

    enumenumName{
       Enumerator_value1,
       Enumerator_value2,
       Enumerator_value3,//  So on};// Creating variable and assigning value
    enumName enam_var = value;

    Here,

    • enumName is the declared variable name of an enum.
    • variableName is the variable name of enumerators (values defined within an enum).

    Example of C++ Enumeration

    In the following example, we are declaring an enumeration type, declaring an enum’s variable/object, and accessing enum constants. Consider the following example −

    #include <iostream>usingnamespace std;// Define an enumeration called Day, for a days of weekenumDay{
       Sunday,// 0
       Monday,// 1
       Tuesday,// 2
       Wednesday,// 3
       Thursday,// 4
       Friday =45,// 45
       Saturday       // 46};intmain(){// Declaring a variable for a day
       Day get1 = Wednesday;
       cout<<get1<<endl;
    
       cout<<Saturday<<endl;return0;}

    Output

    3
    46
    

    Explanation

    In above code, by default explicitly, each day of the week value has been assigned a unique integer value ranging from 0 to 6 to all days of a week, However if we explicitly assign the value 45 to Friday, the enumeration property will cause the sequence to continue from that particular point, therefore the value for remaining Friday and Saturday will become 45 and 46 respectively completing series further on.

    Types of C++ Enumeration

    There are generally two type of enums in C++ −

    1. Unscoped Enums

    Unscoped enums are the traditional form of enumeration in C++. They are defined using the enum keyword with enumerator names declared within the enclosing scope. Since the enumerator names are added to the surrounding scope, which can lead to name collisions if not managed carefully, It can be used directly in the code, which is like a set of labels which represent specific numbers.

    It automatically assigns integer values starting from zero, unless explicitly assigned, and implicitly convertible to integers.

    Example

    #include <iostream>usingnamespace std;enumRGB{
       Red,// 0
       Green,// 1
       Blue    // 2};voidprintColor(RGB color){switch(color){case Red: cout <<"Color is Red";break;case Green: cout <<"Color is Green";break;case Blue: cout <<"Color is Blue";break;}}intmain(){
       RGB myColor = Red;// Here, no need to specify enum nameint value = myColor;// Implicit conversion to int
    
       cout <<"Integer value: "<< value << endl;// Outputs: 0printColor(myColor);// Outputs: Color is Redreturn0;}

    Output

    Integer value: 0
    Color is Red
    

    2. Scoped Enums (enum class)

    Scoped enums, introduced in C++11, are defined using the enum class. They provide better type safety and are more organized, where their enumerator names are scoped within the enum. Which means keeping their labels within a specific group, so you need to mention the group name when you use them. This helps avoid confusion if you have similar labels in different groups.

    Enumerator names are scoped within the enum type, meaning you must use the enum name to access them. No implicit conversion to integers which helps enhance type safety.

    Example

    #include <iostream>usingnamespace std;enumclassColor{
       Red,// Implicitly 0
       Green,// Implicitly 1
       Blue    // Implicitly 2};// Usageintmain(){
       Color myColor = Color::Red;// Must use enum name// int value = myColor;       // Error: no implicit conversion// Explicit conversion to int if neededint value =static_cast<int>(myColor);// value = 0
    
       cout <<"Integer value: "<< value << endl;// Outputs: 0return0;}

    Output

    Integer value: 0
    

    Scoped Vs. Unscoped Enums

    FeaturesUnscoped EnumsScoped Enums (enum class)
    Scope of EnumeratorsGlobal scopeScoped within the enum type
    Name ConflictsPossibleAvoided
    Implicit ConversionYesNo
    Type SafetyLessMore
    Underlying TypeDefault is intCustom type can be specified

    Comparing Enum Values

    Enum values can be compared just like integer comparison. Consider the following example −

    Example

    #include <iostream>usingnamespace std;enumclassColor{
       Red,
       Green,
       Blue
    };intmain(){
       Color myColor = Color::Green;if(myColor == Color::Green){
          cout <<"The color is green!"<< endl;}else{
          cout <<"The color is not green."<< endl;}return0;}

    Output

    The color is green!
    

    Enum as Function Parameters

    You can pass enums as parameters to functions. To pass enum as parameter in function, you need to specify the enum name along its instance.

    Example

    In the following example, we are passing enum as parameter −

    #include <iostream>usingnamespace std;enumclassColor{
       Red,
       Green,
       Blue
    };// Function that takes an enum as a parametervoidprintColor(Color color){switch(color){case Color::Red:
             cout <<"Red"<< endl;break;case Color::Green:
             cout <<"Green"<< endl;break;case Color::Blue:
             cout <<"Blue"<< endl;break;}}intmain(){
       Color myColor = Color::Blue;printColor(myColor);return0;}

    Output

    Blue
    

    Common Use Cases of Enum

    The following are some of the common use cases of enum −

    1. State Management

    enumclassGameState{
       MainMenu,
       Playing,
       Paused,
       GameOver
    };

    2. Configuration Options

    enumclassLogLevel{
       Info,
       Warning,
       Error,
       Debug
    };

    3. Command Types

    enumclassCommand{
       Start,
       Stop,
       Pause,
       Resume
    };

    4. Direction and Movement

    enumclassDirection{
       Up,
       Down,
       Left,
       Right
    };

    Benefits of Using Enums

    • Improved Readability and Maintenance − Enums provide meaningful names for values which makes the code clearer and easier to understand and maintain.
    • Type Safety and Namespace Management − Enums in C++ restrict the assigned values, especially with scoped enums, which reduces errors and avoids name conflicts.
    • Organized Code Structure − Enums helps in enhancing organization by grouping related constants which improve code readability, enforce type safety, create cleaner function interfaces, and facilitate easier refactoring
    • Enhanced Functionality − Enums work smoothly with switch statements and allow explicit control over underlying types with assigned values.

    Limitations of Enums

    Despite of having benefits, it still have few limitations given below −

    • Type Safety Issues − Unscoped enums can cause name conflicts and allow implicit conversion to integers which will increase the risk of errors.
    • Limited Functionality − Enums have a fixed set of values and lack of member functions which cannot be extended at runtime.
    • Debugging Difficulties − Debuggers may display enum values as integers which makes it harder to interpret their meaning.
  • Numbers in C++

    Normally, when we work with Numbers, we use primitive data types such as int, short, long, float and double, etc. The number data types, their possible values and number ranges have been explained while discussing C++ Data Types.

    Defining Numbers in C++

    You have already defined numbers in various examples given in previous chapters. Here is another consolidated example to define various types of numbers in C++ −

    #include <iostream>usingnamespace std;intmain(){// number definition:short  s;int    i;long   l;float  f;double d;// number assignments;
       s =10;      
       i =1000;    
       l =1000000; 
       f =230.47;  
       d =30949.374;// number printing;
       cout <<"short  s :"<< s << endl;
       cout <<"int    i :"<< i << endl;
       cout <<"long   l :"<< l << endl;
       cout <<"float  f :"<< f << endl;
       cout <<"double d :"<< d << endl;return0;}

    When the above code is compiled and executed, it produces the following result −

    short  s :10
    int    i :1000
    long   l :1000000
    float  f :230.47
    double d :30949.4
    

    Math Operations in C++

    In addition to the various functions you can create, C++ also includes some useful functions you can use. These functions are available in standard C and C++ libraries and called built-in functions. These are functions that can be included in your program and then use.

    C++ has a rich set of mathematical operations, which can be performed on various numbers. Following table lists down some useful built-in mathematical functions available in C++.

    To utilize these functions you need to include the math header file <cmath>.

    Sr.NoFunction & Purpose
    1double cos(double);This function takes an angle (as a double) and returns the cosine.
    2double sin(double);This function takes an angle (as a double) and returns the sine.
    3double tan(double);This function takes an angle (as a double) and returns the tangent.
    4double log(double);This function takes a number and returns the natural log of that number.
    5double pow(double, double);The first is a number you wish to raise and the second is the power you wish to raise it t
    6double hypot(double, double);If you pass this function the length of two sides of a right triangle, it will return you the length of the hypotenuse.
    7double sqrt(double);You pass this function a number and it gives you the square root.
    8int abs(int);This function returns the absolute value of an integer that is passed to it.
    9double fabs(double);This function returns the absolute value of any decimal number passed to it.
    10double floor(double);Finds the integer which is less than or equal to the argument passed to it.

    Following is a simple example to show few of the mathematical operations −

    #include <iostream>#include <cmath>usingnamespace std;intmain(){// number definition:short  s =10;int    i =-1000;long   l =100000;float  f =230.47;double d =200.374;// mathematical operations;
       cout <<"sin(d) :"<<sin(d)<< endl;
       cout <<"abs(i)  :"<<abs(i)<< endl;
       cout <<"floor(d) :"<<floor(d)<< endl;
       cout <<"sqrt(f) :"<<sqrt(f)<< endl;
       cout <<"pow( d, 2) :"<<pow(d,2)<< endl;return0;}

    When the above code is compiled and executed, it produces the following result −

    sign(d)     :-0.634939
    abs(i)      :1000
    floor(d)    :200
    sqrt(f)     :15.1812
    pow( d, 2 ) :40149.7
    

    Random Numbers in C++

    There are many cases where you will wish to generate a random number. There are actually two functions you will need to know about random number generation. The first is rand(), this function will only return a pseudo random number. The way to fix this is to first call the srand() function.

    Following is a simple example to generate few random numbers. This example makes use of time() function to get the number of seconds on your system time, to randomly seed the rand() function −

    #include <iostream>#include <ctime>#include <cstdlib>usingnamespace std;intmain(){int i,j;// set the seedsrand((unsigned)time(NULL));/* generate 10  random numbers. */for( i =0; i <10; i++){// generate actual random number
          j =rand();
          cout <<" Random Number : "<< j << endl;}return0;}

    When the above code is compiled and executed, it produces the following result −

    Random Number : 1748144778
    Random Number : 630873888
    Random Number : 2134540646
    Random Number : 219404170
    Random Number : 902129458
    Random Number : 920445370
    Random Number : 1319072661
    Random Number : 257938873
    Random Number : 1256201101
    Random Number : 580322989
  • C++ Constexpr Specifier

    The constexpr specifier was introduced in C++11. It calculates the value of a variable or the return value of a function at compile time. Calculating the values during compilation time improves the performance of the code. It helps in faster execution of programs; since the value is already known during compilation, the runtime overhead is reduced.

    Here is a simple practical example that shows how you can calculate the sum of two numbers using constexpr specifier −

    #include <iostream>usingnamespace std;constexprintsum(int a,int b){return a + b;}intmain(){constexprint x =sum(10,2); 
       cout <<"10 + 2 = "<< x << endl;return0;}

    The output of the above code is as follows −

    10 + 2 = 12
    

    The above example was a simple implementation of constexpr specifier. In this chapter, we will discuss following topics in detail −

    Why We Need constexpr in C++?

    The constexpr is needed in C++ because of the following reasons −

    • Performance optimization − All the overhead of calculating mathematical expressions has now shifted from runtime to compile time. This helps in faster execution of program, as no computation is needed at runtime.
    • Better compile-time constants − Features such as array size, switch case labels, and template parameters require value during compilation which can be provided using constexpr specifier, but there is no guarantee with the const keyword.

    Declaring Variables with constexpr

    You can use a constexpr specifier to declare a variable. The rule for declaring a variable with constexpr is that it should be initialized with a constant expression.

    The valid and invalid syntax for a constexpr variable is given below −

    // -----Valid declarations-----(1)constexprint y =4;(2)constexprint z =7;constexprint x = z;// -----Invalid declarations-----(1)constexprint a;// constexpr variable must be initialized(2)constexprint b = x +2;// x is not a constant expression(3)int z =7;constexprint x = z;// z is not a constant expression

    Example: Valid Declaration

    Below is an example to demonstrate the use of constexpr variable −

    #include <iostream>usingnamespace std;intmain(){constexprint z =7;constexprint x = z; 
       cout <<"value of z: "<< z << endl;
       cout <<"value of x: "<< x << endl;return0;}

    The output of the above code is given below −

    value of z: 7
    value of x: 7
    

    Example: Invalid Declaration

    Here is an example of an invalid declaration of constexpr variable −

    #include <iostream>usingnamespace std;intmain(){int z =7;constexprint x = z; 
       cout <<"value of z: "<< z << endl;
       cout <<"value of x: "<< x << endl;return0;}

    The output of the above code is given below. Here, we can see the error as int z is not a constant.

    main.cpp: In function 'int main()':
    main.cpp:6:23: error: the value of 'z' is not usable in a constant expression
        6 |     constexpr int x = z;
          |                       ^
    main.cpp:5:9: note: 'int z' is not const
        5 |     int z = 7;
          |         ^
    

    Functions with Constexpr Specifier

    You can use constexpr specifier to define a function. Given below are the rules that you should follow while using constexpr to define a function:

    • The parameter and return type of function should be of literal type. For example: int, double, char, and bool are valid, whereas string and vector are invalid return types.
    • The function should have only one return statement till C++11. From C++14 onwards, it can have multiple return statements.
    • The function cannot have any side effects. For example: the function can not have input/output (cin/cout) operations, can not modify global and static variables.
    • A constexpr function can only call other constexpr functions, not simple functions.

    Factorial Calculation with Constexpr

    Here is an example to implement a function with constexpr specifier to calculate the factorial of 5 at compile-time −

    #include <iostream>usingnamespace std;constexprlonglongfactorial(int n){return(n <=1)?1: n *factorial(n -1);}intmain(){// Computed at compile-timeconstexprlonglong fact =factorial(5); 
       cout <<"Factorial of 5 = "<< fact << endl;return0;}

    The output of the above code is given below −

    Factorial of 5 = 120
    

    Fibonacci Calculation with Constexpr

    Below is an example to find the fibonacci number. In this example, multiple return statements are used which was not valid before C++14.

    #include <iostream>usingnamespace std;// Fibonacci using iterationconstexprlonglongfibonacci(int n){if(n <=1)return n;longlong prev =0, curr =1;for(int i =2; i <= n;++i){longlong next = prev + curr;
          prev = curr;
          curr = next;}return curr;}intmain(){// Computed at compile-timeconstexprlonglong fib =fibonacci(10);  
       cout <<"Fibonacci of 10 = "<< fib << endl;return0;}

    The output of the above code is given below −

    Fibonacci of 10 = 55
    

    Constructors with Constexpr Specifier

    The constexpr specifier can be used to create classes, objects, and declare a constructor. To declare a constructor, we must follow rules given below −

    • You can only use literal type parameters in a constructor.
    • You cannot have virtual base classes or virtual member functions in classes.
    • Only other constexpr constructors and functions can be called.
    • Classes with constexpr constructors cannot have mutable data members.
    • A constructor with constexpr cannot have try/catch blocks or throw expressions.

    The example below demonstrates how to use constexpr specifier with a constructor for converting a temperature given in celsius to fahrenheit −

    #include <iostream>usingnamespace std;classTemperature{double celsius;public:// constexpr constructorconstexprTemperature(double c):celsius(c){}// convert to FahrenheitconstexprdoubletoFahrenheit()const{return(celsius *9.0/5.0)+32.0;}// get CelsiusconstexprdoublegetCelsius()const{return celsius;}};intmain(){// Compile-time constant objectconstexpr Temperature t1(25.0);
       cout <<"Compile time temperature conversions:"<< endl;// computed at compile-timeconstexprdouble f1 = t1.toFahrenheit();
       cout << t1.getCelsius()<<"°C = "<< f1 <<"°F"<< endl;// Runtime objectdouble input =100.0;
       Temperature t2(input);// runtime
       cout <<"\nRuntime temperature conversions:"<< endl;
       cout << t2.getCelsius()<<"°C = "<< t2.toFahrenheit()<<"°F"<< endl;return0;}

    The output of the above code is as follows −

    Compile time temperature conversions:
    25°C = 77°F
    
    Runtime temperature conversions:
    100°C = 212°F
    

    Use Cases of Constexpr Specifier

    There are multiple practical use cases of constexpr specifier in various scenarios. Here are some of the uses mentioned below −

    Array Sizes at Compile-time

    In this example, constexpr provides the array size to print the numbers at compile-time using bufferSize() function.

    #include <iostream>usingnamespace std;constexprintbufferSize(){return10;}intmain(){// valid as the buffer size is // known at compile-timeint arr[bufferSize()];for(int i =0; i <bufferSize(); i++){
          arr[i]= i;}
    
       cout <<"Array elements: ";for(int i =0; i <bufferSize(); i++){
          cout << arr[i]<<" ";}
       cout << endl;return0;}

    The output of the above code is given below −

    Array elements: 0 1 2 3 4 5 6 7 8 9
    

    Mathematical Computations

    The following example calculates the 10th power of 2. The power is calculated at compile-time rather than run-time. This reduces the run-time overhead of the program.

    #include <iostream>usingnamespace std;constexprlonglongpower(int base,int exp){longlong result =1;for(int i =0; i < exp;++i){
          result *= base;}return result;}intmain(){constexprlonglong n =power(2,10);// Calculated at compile time
       cout <<"2^10 = "<< n << endl;return0;}

    The output of the above code is given below −

    2^10 = 1024
    

    Using Constexpr in Switch Case Labels

    Here is an example to pre-define the label of switch case using constexpr specifier. If we remove constexpr, it will give an error.

    #include <iostream>usingnamespace std;constexprintchoice(){return3;// value known at compile-time}intmain(){switch(3){casechoice():   
             cout <<"Matched case 3"<< endl;break;default:
                cout <<"Default case"<< endl;}return0;}

    The output of the above code is given below −

    Matched case 3
    

    Difference between Const and Constexpr

    The following table lists the differences between const keyword and constexpr specifier −

    constconstexpr
    Values can be evaluated at compile-time or run-time.Values must be evaluated at compile time.
    The variables can be initialized with compile time or run time values.We initialize variables with compile-time constant expressions only.
    There may be runtime overhead.There is zero runtime overhead as evaluation occurs at compile-time.

    Limitations of Constexpr Specifier

    There are various advantages of using a constexpr specifier such as reducing the runtime overhead and increasing the performance but there are some limitations too that are mentioned below:

    • The input and output operations (cin/cout) cannot be used in the functions declared with constexpr specifier.
    • It does not support dynamic memory allocation.
    • One constexpr function can only call another constexpr function. We can not call any other normal functions.
    • There is no support for exception handling.
    • Function parameters or return type can only be of literal type.

    Conclusion

    In this chapter, we explained the use of constexpr specifier in detail. It calculates the value at compile-time, reducing the run-time overhead. We discussed the rules to use constexpr with variables, functions, and constructors with code examples. It offers the advantage of increasing the performance, but it also has some limitations that we highlighted at the end of the chapter.

  • Storage Classes in C++

    A storage class defines the scope (visibility) and life-time of variables and/or functions within a C++ Program. These specifiers precede the type that they modify. There are following storage classes, which can be used in a C++ Program

    • auto
    • register
    • static
    • extern
    • mutable

    The auto Storage Class

    The auto storage class is the default storage class for all local variables.

    Example

    Below is the example of auto storage class −

    {int mount;autoint month;}

    The example above defines two variables with the same storage class, auto can only be used within functions, i.e., local variables.

    The register Storage Class

    The register storage class is used to define local variables that should be stored in a register instead of RAM. This means that the variable has a maximum size equal to the register size (usually one word) and can’t have the unary ‘&’ operator applied to it (as it does not have a memory location).

    Example

    Below is the example of register storage class −

    {registerint  miles;}

    The register should only be used for variables that require quick access such as counters. It should also be noted that defining ‘register’ does not mean that the variable will be stored in a register. It means that it MIGHT be stored in a register depending on hardware and implementation restrictions.

    The static Storage Class

    The static storage class instructs the compiler to keep a local variable in existence during the life-time of the program instead of creating and destroying it each time it comes into and goes out of scope. Therefore, making local variables static allows them to maintain their values between function calls.

    The static modifier may also be applied to global variables. When this is done, it causes that variable’s scope to be restricted to the file in which it is declared.

    In C++, when static is used on a class data member, it causes only one copy of that member to be shared by all objects of its class.

    Example

    Below is the example of static storage class −

    #include <iostream>// Function declarationvoidfunc(void);staticint count =10;/* Global variable */main(){while(count--){func();}return0;}// Function definitionvoidfunc(void){staticint i =5;// local static variable
       i++;
       std::cout <<"i is "<< i ;
       std::cout <<" and count is "<< count << std::endl;}

    When the above code is compiled and executed, it produces the following result −

    i is 6 and count is 9
    i is 7 and count is 8
    i is 8 and count is 7
    i is 9 and count is 6
    i is 10 and count is 5
    i is 11 and count is 4
    i is 12 and count is 3
    i is 13 and count is 2
    i is 14 and count is 1
    i is 15 and count is 0
    

    The extern Storage Class

    The extern storage class is used to give a reference of a global variable that is visible to ALL the program files. When you use ‘extern’ the variable cannot be initialized as all it does is point the variable name at a storage location that has been previously defined.

    When you have multiple files and you define a global variable or function, which will be used in other files also, then extern will be used in another file to give reference of defined variable or function. Just for understanding extern is used to declare a global variable or function in another file.

    The extern modifier is most commonly used when there are two or more files sharing the same global variables or functions as explained below.

    Example

    Below is the example of extern storage class −

    First File: main.cpp

    #include <iostream>int count ;externvoidwrite_extern();main(){
       count =5;write_extern();}

    Second File: support.cpp

    #include <iostream>externint count;voidwrite_extern(void){
       std::cout <<"Count is "<< count << std::endl;}

    Here, extern keyword is being used to declare count in another file. Now compile these two files as follows −

    $g++ main.cpp support.cpp -o write
    

    This will produce write executable program, try to execute write and check the result as follows −

    $./write
    5
    

    The mutable Storage Class

    The mutable specifier applies only to class objects, which are discussed later in this tutorial. It allows a member of an object to override const member function. That is, a mutable member can be modified by a const member function.

  • C++ Modifier Types

    C++ allows the char, int, and double data types to have modifiers preceding them. A modifier is used to alter the meaning of the base type so that it more precisely fits the needs of various situations.

    The data type modifiers are listed here −

    • signed
    • unsigned
    • long
    • short

    The modifiers signed, unsigned, long, and short can be applied to integer base types. In addition, signed and unsigned can be applied to char, and long can be applied to double.

    The modifiers signed and unsigned can also be used as prefix to long or short modifiers. For example, unsigned long int.

    C++ allows a shorthand notation for declaring unsigned, short, or long integers. You can simply use the word unsigned, short, or long, without int. It automatically implies int. For example, the following two statements both declare unsigned integer variables.

    unsigned x;unsignedint y;

    To understand the difference between the way signed and unsigned integer modifiers are interpreted by C++, you should run the following short program −

    #include <iostream>usingnamespace std;/* This program shows the difference between
       * signed and unsigned integers.
    */intmain(){shortint i;// a signed short integershortunsignedint j;// an unsigned short integer
    
       j =50000;
    
       i = j;
       cout << i <<" "<< j;return0;}

    When this program is run, following is the output −

    -15536 50000
    

    The above result is because the bit pattern that represents 50,000 as a short unsigned integer is interpreted as -15,536 by a short.

    Type Qualifiers in C++

    The type qualifiers provide additional information about the variables they precede.

    Sr.NoQualifier & Meaning
    1constObjects of type const cannot be changed by your program during execution.
    2volatileThe modifier volatile tells the compiler that a variable’s value may be changed in ways not explicitly specified by the program.
    3restrictA pointer qualified by restrict is initially the only means by which the object it points to can be accessed. Only C99 adds a new type qualifier called restrict.