Click here to Skip to main content
15,890,995 members
Articles / Programming Languages / C++

LConst PConst - Exploring the const Keyword in C++

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
17 Jul 2023CPOL4 min read 9.7K   2   2
Some cases when const keyword in C++ doesn't protect us from modifications at all
const qualifier does not always work the way we expect it to. In this article, we will see some cases when the const doesn't protect us from modifications at all.

The const keyword is widely used within almost every C++ program. It symbolizes the idea of a constant variable, object, function, and more, and is meant to forbid us from manipulating and changing the content of a memory location within a certain scope. However, as we learned before, “There is nothing more deceptive than an obvious fact.” (Sherlock Holmes).

Const Rules

In this article, I won’t dive deeply into the const rules, but just move briefly on const ideas I consider as crucial for the point I want to talk about.

Let’s start with the basic rules of const. The most basic const example is a constant variable:

C++
const int a = 42;

In this example, we initialize a constant integer with a value of 42. Any const variable should be initialized at the creation stage (due to the inability to initialize it later). Once the variable is initialized, there is no way to change it (Well… const_cast but it didn’t get into this article scope).

Const Location

const keyword can be written in multiple locations of the type:

C++
const int a = 5;
int const b = 6;

The result is the same, in both cases, the variables are constant. However, this ability has a great impact when pointers join the picture.

C++
int val;
const int* a = &val;

In this example, we initialize a mutable (not const) int variable named val, and a const pointer to it called a. Now, I say const because it’s only a partial const pointer, here is actually applied only to the pointer’s value, not the pointer itself, which means that we can modify this pointer to point to another variable, but we won’t be able to modify the variable content through this pointer. For example:

C++
int val;
const int* a = &val;

// *a = 5; // Won't compile. The integer content is protected by the const qualifier.

int val2;
a  = &val2; // Works just fine.

Now, this also means that we can create the pointer variable without assigning a value to it at the creation stage:

C++
// const val; // doesn't compile
const int* a; // Works.

If we want to protect the pointer, we have to change the const qualifier location. Let’s inspect the different possible locations for the const qualifier in this example:

C++
const int* a; // the int content is being protected.
int const* b; // the int content is being protected.

int val;
int* const c = &val; // the pointer is being protected, 
                     // and therefore has to be initialized at the creation stage.

We can combine the different const locations as long as they don’t repeat the meaning in the same variable:

C++
int val;
const int* const a = &val; // both the pointer and its content are protected.
int const* const b = &val; // both the pointer and its content are protected.
// const int const c;      // Won't compile because the secound const qualifier 
                           // has the same meaning of the first const qualifier.

The rule is simple if a const is the first qualifier in the type, the const will be applied to the next element of it. Otherwise, it’ll always be applied to the previous component of it.

Side note: A constant pointer equivalent is a reference variable, for example:

C++
int val;
int &a = val;
int *const b = &val;

Variables a and b here have the same meaning, they are both protecting the pointer from changes.

Class Const Member Functions

Now that we know the rules for constant variables, let’s talk about constant functions.

Constant functions are defined as functions that cannot modify a class’s variables. For example:

C++
class A {
public:
    void func() const {
        // a = 5; // Won't compile
    }

private:
    int a;
};

Everything seems great, right? Let’s inspect another case:

C++
class A {
public:
    void func() const {
        // a = &val; // Won't compile
    }

private:
    int *a;
    int val;
};

So far, everything seems just right. If a function is const, the class’s members can not be modified. It’s time to break this rule a little:

C++
class A {
public:
    A() : a(&val) {}

    void func() const {
        *a = 42; // Wait, WHAT?!
    }

private:
    int *a;
    int val;
};

I guess it’s the right time to go back to the quote, “There is nothing more deceptive than an obvious fact.” of Sherlock. The constant function only protects the last element of the class’s members’ types. So in this example, we modified a member variable content through a pointer member inside a constant member function.

Marking this function as a const function is a mistake that might lead to bugs if someone will relay on this const qualifier. This function actually performs a hidden const_cast and it shouldn’t. In order to fix this issue, the right thing would be to remove the const qualifier from this function.

Code inspection tools sometimes might advise us to add a const qualifier to this function, because the compiler allows that, but we shouldn’t. Adding a const qualifier to a class’s function should always be done only after careful consideration of whether this function modifies the class’s state/variables or not.

Let’s see an even more confusing example:

C++
class A {
public:
    A() : a(val) {}

    void func() const {
        a = 42; // Oh no...
    }

private:
    int &a;
    int val;
};

This time, we even don’t see the * when modifying the content. And it’s not even a UB! It’s totally legal by the language’s standard.

Conclusion

The title LConst PConst stands for Logical Const vs. Physical Const. We should always prefer to use the logical one over the physical one, and the compiler doesn’t help us this time.

I didn’t dive into more aspects of the const qualifier in this article, but I might do it in future articles. If you have a specific request, feel free to contact me on the dedicated channels of this blog like Discord and Telegram.

Feel free to leave your opinions in the comments section.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Israel Israel
Senior C++ developer.
C++ Senioreas blog writer.
Passionate about investigating the boundaries and exploring the advanced capabilities of C++.

Comments and Discussions

 
SuggestionSometimes you need const... Pin
Paolo Messina24-Jul-23 0:07
professionalPaolo Messina24-Jul-23 0:07 
If you want to have const objects of some class you have defined, you'll probably end up needing some const member function. One problem that may arise is that you have some member variables that you might need to change and some that you want to be immutable when the object is const. Those are the cases when I struggle finding a good compromise. You probably need refactoring, but sometimes it tends to become complicated.

This article is a nice and quick reading but I feel like it's only the tip of the iceberg. It woud be nice if you expanded the article further, I think there is a lot to talk about with regards to the const keyword. Smile | :)
Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)

GeneralRe: Sometimes you need const... Pin
Coral Kashri12-Apr-24 13:47
Coral Kashri12-Apr-24 13:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.