Showing posts with label const. Show all posts
Showing posts with label const. Show all posts

C++ Constant : Types And Uses

C++ Constant : Types And Uses

What is Constant ?

As similar to variable constant are data storage location. As the name implies constant's value do not change. They remain constant throughout the program. Unlike variable whose value can be changed anywhere in the program.

There are two types of constant in C++. They are as follows :
1) Literal Constant
float PI=3.14;
The value that is directly typed into the program is called literal constant.
Here 3.14 is called literal constant. You cannot assign a value to 3.14.
2) Symbolic Constant
Symbolic Constant are represented by name.

There are two ways to declare a symbolic constant. They are as follows :
1) By using preprocessor directive #define.
This is old way of declaring constant. It has now became obsolete way.
2) By using keyword const.
This way is appropriate way to declare constant.

A C++ Program example that demonstrate the use of constant by using preprocessor directive #define


/* Area Of Circle Program */

#include <iostream>

#define PI 3.14

using std::cout;
using std::cin;
using std::endl;

int main ()
{

    int r;

    cout << "Find the area of circle." << endl;
    cout << "Enter radius : ";
    cin >> r;

    float area = PI * r * r;

    cout << "The area of circle of radius     " << r << " is "
    << area << endl;

    return 0;
}


A C++ Program example that demonstrate the use of constant by using the keyword const


/* Area Of Circle Program */

#include <iostream>

using std::cout;
using std::cin;
using std::endl;

const float PI = 3.14;

int main ()
{

    int r;

    cout << "Find the area of circle." << endl;
    cout << "Enter radius : ";
    cin >> r;

    float area = PI * r * r;

    cout << "The area of circle of radius " << r << " is " << area
    << endl;

    return 0;
}


Another C++ Program example that demonstrate the use of constant


/* Program that calculate total income of the year */

#include <iostream>

using std::cout;
using std::endl;

int main ()
{

    const int salary = 20000;
    float tax = (float) 10 / 100 * salary;        // tax is 10% of salary
    float monthlyIncome = salary - tax;
    // bonus is 5% of salary
    float yearlyBonus = (float) 5 / 100 * salary;
    float yearlyIncome = (monthlyIncome*12) + yearlyBonus;

    cout << "My yearly income is " << yearlyIncome << endl;

    return 0;
}


In the above program example, salary is declared as constant of type int. You can assign a value to constant only at the declaration time. This value could not be changed later on the program. If you do you will get compiler error " assignment of read-only variable 'salary' " and your program wont compile.

C++ Notes :

(1) The way to declare a string constant with #define :
#define HOBBY "Programming"
String constant must be enclosed with double-inverted commas.

(2) The way to declare a character constant with #define :
#define AGREE 'y'
Character constant must be enclosed with single-inverted commas.

(3) Numeric type of data are not enclosed with inverted commas.

(4) The advantage of using const keyword is that you can create constant of various data types by mentioning it explicitly.
For example :
const unsigned short int myVal = 40;

String literals in C++

Here's a short one on string literals in C++. Ask yourself: what is their type? It is 'array of n const char', correct! So, we might think:

char* literal = "Hello World!";

would be "invalid"/"illegal" in C++. But you'd be surprised it is not and even Comeau online compiles it successfully without even a warning.

The C++ standard, however, tries to protect you hinting that the above is wrong by stating that it is a deprecated feature in C++ that probably was allowed to keep backward compatibility with C.

Here is what the standard says as part of section [2.13.4/2]:

[quote]
A string literal that does not begin with u, U, or L is an ordinary string literal, also referred to as a narrow string literal. An ordinary string literal has type “array of n const char”, where n is the size of the string as defined below; it has static storage duration (3.7) and is initialized with the given characters.
[/quote]

So, the following would have definitely been invalid in C++:

char* literal = "Hello World!";

"Hello World!" is an array of 13 [spooky :-)] constant characters. 'literal' is the pointer to the first element of the array and since that element is const, the pointer cannot be declared as a non-const char*. The pointer has to be of the type 'pointer to a const char'.

But as mentioned above, to have backward compatibility with C where the above works an implicit conversion is defined for array to pointer conversion where a string literal would be converted to an r-value of type "pointer to a char". The standard mentions this in section [4.2/2]:

[quote]
A string literal (2.13.4) with no prefix, with a u prefix, with a U prefix, or with an L prefix can be converted to an rvalue of type “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, or “pointer to wchar_t”, respectively. In any case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue. [ Note: this conversion is deprecated. See Annex D. —end note ]
[/quote]

But, the thing to be happy about is the note above, that is re-iterated in Annexure D section [D.4/1] as:

[quote]
The implicit conversion from const to non-const qualification for string literals (4.2) is deprecated.
[/quote]

So, best is to keep the good habit of declaring the pointer to the literal as a pointer to a const. :-)

[The C++ standard's draft version used for quotes above, has document number : N2315=07-0175 dated 2007-06-25]

const in C and C++

Globals are usually bad but not always. Consider C code that is to be migrated to C++ and has a bunch of global constants. As soon as that code is worked upon and is compiled as C++, those const globals will cause the compiler to start emitting "undefined reference" or similar errors.

This is because of fundamental different between how const is treated in C and C++ in the context of linkage.

In C, apart from the fact that const are non-modifiable variables, they are the same as any other variables. In C++, const have different linkage as well. C++ const objects/variables have internal linkage and hence you get unresolved symbol error when you try to access something in a different compilation unit having iternal linkage from another compilation unit.

The solution is to include the extern declaration following right after you define the const variables, the same one that you put in another files or simply define the variable as extern const instead of just const.

Quiz : function pointers as template arguments

Yesterday, I came across a piece of template code that took me a little by surprise (because I had not come across something like this before) but I was able to put my reasoning through. I will share the code first:

[CODE]

#include <iostream>

template<typename T>
void foo(const T&)
{
               std::cout << "const";
}

template<typename T>
void foo(T&)
{
               std::cout << "non-const";
}

void bar() { }

int main()
{
               foo(bar);
}

The question was - what would the program print? Will the argument "bar" resolve as a parameter to the first template having argument type "const F&" or to the second template having non-const argument type "F&"?

The easiest way to check for the resolution is to ask for explicit template instantiation of the "foo" function template. How? Here is how:

[CODE]

int main()
{
               typedef void (*f_ptr)(); //create a typedef for functions like bar
                                                               //taking no arguments and having return type as void.
               foo<const f_ptr>(bar); //1
               foo<f_ptr>(bar); //2
}

After that, just remove one of the "foo" templates. So, the code to check for compilation is this:

[CODE]
#include<iostream>

template<typename T>
void foo(T&)
{
               std::cout << "non-const";
}

void bar() { }

int main()
{
               typedef void (*f_ptr)(); //create a typedef for functions like bar
                                                               //taking no arguments and having return type as void.
               foo<const f_ptr>(bar); //1
               foo<f_ptr>(bar); //2
}


If you removed the second template, code compiles fine. But if you kept the second one and removed the first one, you will see that the compilation fails for mis-match in the argument type in statement "//2".

Problem solved. Isn't it? What does this tell about the argument "bar" ? It tells that it is a constant. And hence the call in the initial sample code would resolve to the template instance having the argument type declared const. It is in a way similar to any other type constants, for example 5, 100, 2000 are integral constants, they are literals. And when you declare something as say int i; here i is a variable that can be modified. but 5, 100, or 2000 cannot be. In our initial code, both the templates were capable of instantiating the right function for the argument. In both's presence, the argument match has to be exact and hence instantiation happens from the const one but in its absense the instantiation can happen even with the non-const one as the call can help the template to instantiate over type const f_ptr instead of just f_ptr (which is the case for the const one).

Functions pointers when being passed by taking the address of the function directly is a value of const function pointer type.

Check out this stream