Author: Chloé Lourseyre
A bit of terminology
In the title, I used the word “const” because const
is a keyword every C++ developer knows (I hope). However, the generic concept this article is about is actually called “immutability”, and not “constness”. const
is a keyword used in some languages (C++, Javascript, etc.) but the concept of immutability exists in other languages (in some of them, there is even no keyword attached to the concept, like in Rust).
To be a little more inclusive, I will use the words immutable and immutability instead of const and constness.
Nota bene: in C++ the keyword const
does not completely refer to immutability. For instance, you can use the keyword const
in a function prototype to indicate you won’t modify it, but you can pass a mutable object as this parameter. However, this article only refers to const
as a way to render data immutable, so I would be grateful if you ignore situations where const
does not refer to inherently immutable data. That is also another reason I wish to use the word immutable and not const.
Some very good reasons to make your variables immutable
Safety
What is called const-correctness in C++ is the fact that if you state that a given variable is immutable, then it won’t be modified.
Const-correctness is reached when you use the const
keyword to tell when a variable won’t be modified. Then, you won’t be able to inadvertently modify something you shouldn’t and you give insight to the compiler and the developers of the data you manipulate.
Documentation
These five characters tell a lot. If you find yourself in a reliable code, where you are sure that const
is used properly, then this keyword -or its absence- serves as an indication of the intended mutability of the variable.
Optimization
The compiler can (and will) optimize the code according to the const
directives.
Knowing that some values are immutable, the compiler will be able to make assumptions and use syntactic shortcuts.
You can feed on J. Turner’s talk on the subject of practical performance: Jason Turner: Practical Performance Practices – YouTube.
Source
If you still doubt that immutability is useful in C++ (or are just curious, which you should be), please read these:
Are there downsides to using immutability whenever possible?
Since immutability is a technical restriction, there is no technical downside to using it.
However, there may be some non-technical downside of using const
. First, it increases the verbosity. Even if it’s a 5-letters word that often has the same color as the type (in most IDEs), some people might find this impairing. There is also inertia: if you need to modify the variable, you’ll have to go and remove the keyword. Some may consider it a safeguard, others an impairment.
Are these arguments enough to not use immutability as soon as you can? It’s up to you, but in my humble opinion, I think not.
Other languages?
Other languages make variables immutable by default.
One famous example is the Rust language. In Rust, you don’t have the const
keyword. Instead, every variable is by default immutable, and you can add the mut
keyword to make them mutable.
For instance, in Rust, the following code doesn’t compile:
fn main() {
let foo = 2;
println!("Foo: {}", foo);
foo += 40; // error[E0384]: cannot assign twice to immutable variable `foo`
println!("Foo: {}", foo);
}
But this is correct:
fn main() {
let mut foo = 2;
println!("Foo: {}", foo);
foo += 40;
println!("Foo: {}", foo);
}
The reason mentioned in the Rust Handbook is the following: “This is one of many nudges Rust gives you to write your code in a way that takes advantage of the safety and easy concurrency that Rust offers.”
Rust is a pretty modern language that aims to provide safety to the developer along with top-tier performances. I agree with its intention to make variables immutable by default because of the safety it brings.
Is it reasonable to change the standard to make variables immutable by default?
If one fateful day the C++ committee decides to make every variable const by default… no one will upgrade to the vNext of C++.
Retro compatibility is important to smooth the transition between vPrevious and vNext. There are few historical examples where a feature has been removed from the standard, and for each, there is a really good reason why (often being years of deprecation).
But we can’t seriously consider removing the keyword const
and add the keyword mut
just for the sake of what is, in the end, syntactic sugar.
What to do then?
I for one chose to use the const
keyword every single time I declare a variable. I only remove it when I actually write an instruction that requires it to be mutable (or when the compiler realizes it needs to modify a constant variable and outputs an error). This is a little blunt, but in my opinion, it’s the best way to ensure to catch the habit of putting const
by default.
Of course, you may try to be smart at the moment you declare the variable and try to foresee the use you’ll make of it, but I guarantee you will not be 100% reliable and miss opportunities to make variables immutable.
So here’s my advice: until you catch the habit of putting const
everywhere, use const
by default without double thinking. You may have regular easy-to-solve compilation errors, but you’ll catch a good habit.
And what about constexpr?
constexpr
is a modifier that indicates that the value of a variable can be evaluated at compile time. Its purpose is to transfer some evaluation to the compilation phase.
In a sense, it is stronger immutability than the keyword const
. All I said in this article about immutability applies to constexpr
: use it as often as you can.
However, unlike const
, I don’t advise trying to use it everywhere and see afterward if it needs to be removed, since it’s way rarer and a bit more obvious to use. But keep in mind to check if you can use constexpr
every time to declare a variable.
Thank you for reading and see you next week!
Author: Chloé Lourseyre
Good one. I’m 10000% with you. Recently, maybe 3 years ago I started const everything (and constexpr whenever possible) and it’s just a simple habit. Thank you again for such a nice writeup.
Are you referring to « const » being a four letter word, two times, in this text? Also, great read, thanks for the write-up!
🤔 If a variable (something that “varies”) is constant (meaning it never “varies”). So the article title is an oxymoron. How about constant “field” or constant “definition”? That said, I default to putting “const” before every local definition, and I wish that “const” alone implied “auto” too. That is “const x = 5;” would just work as “const auto x = 5”. I believe this should be safe now in the grammar, given default int has been deprecated for a long while.
I gave once a talk, that const should be a type.
And I still think that should be the case.
https://www.youtube.com/watch?v=oqGxNd5MPoM
is there a new op dot proposal ?
I wish there would be one …
that’s the only thing I can find https://isocpp.org/blog/2016/02/a-bit-of-background-for-the-operator-dot-proposal-bjarne-stroustrup
Great, I think this article will fundamentally change my programming style 🙂 Has anyone gone so far to make all parameters passed by value consts?
e.g.
int BigAlgo(const int x, const int y, const int z);
me. all const. including top level. even when pass by value. all, everything is const. only in def, not in decl (tidy will warn)