How far do you go with const
? Do you just make functions const
when necessary or do you go the whole hog and use it everywhere? For example, imag
There is a good discussion on this topic in the old "Guru of the Week" articles on comp.lang.c++.moderated here.
The corresponding GOTW article is available on Herb Sutter's web site here.
The answer by @Adisak is the best answer here based on my assessment. Note that this answer is in part the best because it is also the most well-backed-up with real code examples, in addition to using sound and well-thought-out logic.
const
. All it does is:
const
everywhere can hinder this.const
unnecessarily clutters the code with const
s everywhere, drawing attention away from the const
s that are truly necessary to have safe code.const
is critically important when needed, and must be used, as it prevents undesired side effects with persistent changes outside the function, and therefore every single pointer or reference must use const
when the parm is an input only, not an output. Using const
only on parameters passed by reference or pointer has the additional benefit of making it really obvious which parameters are pointers or references. It's one more thing to stick out and say "Watch out! Any param with const
next to it is a reference or pointer!".(From the "Google C++ Style Guide")
For a function parameter passed by value, const has no effect on the caller, thus is not recommended in function declarations. See TotW #109.
Using const on local variables is neither encouraged nor discouraged.
Source: the "Use of const" section of the Google C++ Style Guide: https://google.github.io/styleguide/cppguide.html#Use_of_const. This is actually a really valuable section, so read the whole section.
Note that "TotW #109" stands for "Tip of the Week #109: Meaningful const in Function Declarations", and is also a useful read. It is more informative and less prescriptive on what to do, and based on context came before the Google C++ Style Guide rule on const
quoted just above, but as a result of the clarity it provided, the const
rule quoted just above was added to the Google C++ Style Guide.
Also note that even though I'm quoting the Google C++ Style Guide here in defense of my position, it does NOT mean I always follow the guide or always recommend following the guide. Some of the things they recommend are just plain weird, such as their kDaysInAWeek-style naming convention for "Constant Names". However, it is still nonetheless useful and relevant to point out when one of the world's most successful and influential technical and software companies uses the same justification as I and others like @Adisak do to back up our viewpoints on this matter.
clang-tidy
, has some options for this:A. It's also worth noting that Clang's linter, clang-tidy
, has an option, readability-avoid-const-params-in-decls
, described here, to support enforcing in a code base not using const
for pass-by-value function parameters:
Checks whether a function declaration has parameters that are top level const.
const values in declarations do not affect the signature of a function, so they should not be put there.
Examples:
void f(const string); // Bad: const is top level. void f(const string&); // Good: const is not top level.
And here are two more examples I'm adding myself for completeness and clarity:
void f(char * const c_string); // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string); // Good: const is not top level. [This makes what is being _pointed to_ const]
B. It also has this option: readability-const-return-type
- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html
I'd simply copy and paste this into my style guide:
[COPY/PASTE START]
const
on function parameters passed by reference or pointer when their contents (what they point to) are intended NOT to be changed. This way, it becomes obvious when a variable passed by reference or pointer IS expected to be changed, because it will lack const
. In this use case const
prevents accidental side effects outside the function.const
on function parameters passed by value, because const
has no effect on the caller: even if the variable is changed in the function there will be no side effects outside the function. See the following resources for additional justification and insight:
const
[ie: const
on parameters passed by value] on function parameters in declarations that are not definitions (and be careful not to copy/paste a meaningless const
). It is meaningless and ignored by the compiler, it is visual noise, and it could mislead readers" (https://abseil.io/tips/109, emphasis added).
const
qualifiers that have an effect on compilation are those placed in the function definition, NOT those in a forward declaration of the function, such as in a function (method) declaration in a header file. const
[ie: const
on variables passed by value] on values returned by a function.const
on pointers or references returned by a function is up to the implementer, as it is sometimes useful.clang-tidy
options:
Here are some code examples to demonstrate the const
rules described above:
const
Parameter Examples:
(some are borrowed from here)
void f(const std::string); // Bad: const is top level.
void f(const std::string&); // Good: const is not top level.
void f(char * const c_string); // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string); // Good: const is not top level. [This makes what is being _pointed to_ const]
const
Return Type Examples:
(some are borrowed from here)
// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();
// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();
[COPY/PASTE END]
I do not use const for value-passed parametere. The caller does not care whether you modify the parameter or not, it's an implementation detail.
What is really important is to mark methods as const if they do not modify their instance. Do this as you go, because otherwise you might end up with either lots of const_cast<> or you might find that marking a method const requires changing a lot of code because it calls other methods which should have been marked const.
I also tend to mark local vars const if I do not need to modify them. I believe it makes the code easier to understand by making it easier to identify the "moving parts".
Ah, a tough one. On one side, a declaration is a contract and it really does not make sense to pass a const argument by value. On the other hand, if you look at the function implementation, you give the compiler more chances to optimize if you declare an argument constant.
To summarize:
std::vector::at(size_type pos)
. What's good enough for the standard library is good for me.the thing to remember with const is that it is much easier to make things const from the start, than it is to try and put them in later.
Use const when you want something to be unchanged - its an added hint that describes what your function does and what to expect. I've seen many an C API that could do with some of them, especially ones that accept c-strings!
I'd be more inclined to omit the const keyword in the cpp file than the header, but as I tend to cut+paste them, they'd be kept in both places. I have no idea why the compiler allows that, I guess its a compiler thing. Best practice is definitely to put your const keyword in both files.