Local variables which are not changed after initialisation should be const, excluding

Rule

  • Unmodified stack value variables should be const. This is based on C++ core guidlines for “const”. This is about readability/understandability. This shouldn’t include parameter values, which can be handled by rule S994 which is MISRA C++2008 rule 7-1-1. The C++ core guidelines excludes the cases of parameters. This rule should only handle values, as there are other rules which apply to pointers and references that can be made const.

    Reasons for the rule: (From the C++ core guidelines):

  • Con.1
    Immutable objects are easier to reason about, so make objects non-const only when there is a need to change their value. Prevents accidental or hard-to-notice change of value.
    Con.4
    Prevent surprises from unexpectedly changed object values.

Example code:

void f()
{
    int x = 7;
    const int y = 9;

    for (;;) {
        // ...
    }
    // ...
}

As x is not const, we must assume that it is modified somewhere in the loop.

The key case is for value variables, but there is room for also a new rule for pointers (for when not wanting to use S994)

Non-compliant code

for (int i : c) cout << i << '\n';          // BAD: just reading

Compilant Code (fixing the above noncompliant code)

for (const int i : c) cout << i << '\n';    // just reading: const 

Exceptions:

From the C++ core guidlines, it shouldn’t apply to variables which are function parameters, and variables which are returned. Currently S994 applies to function parameters, so S994 can be used instead of this rule if someone wants the stricter MISRA rule. But this new rule is needed when S994 is not wanted.

Exception examples:

A local variable that is returned by value and is cheaper to move than copy should not be declared const because it can force an unnecessary copy.

std::vector<int> f(int i)
{
    std::vector<int> v{ i, i, i };  // const not needed
    return v;
}

Function parameters passed by value are rarely mutated, but also rarely declared const. To avoid confusion and lots of false positives, don’t enforce this rule for function parameters.

void g(const int i) { ... }  // pedantic

Also this rule should just apply to value variables, as there are other rules which apply to references e.g. C5350. Also it shouldn’t apply to global variables which have a rule S5421.

Should this apply to pointers. S994 applies to pointer parameters, but not pointer local variables. This new rule could apply to those cases, but it may be better to other new rules as well for those cases for when S994 is not suitable.

Exhaustive case:

void YetAnotherFunc(int* view) // Noncompliant //S994 - and new rule or another new rule for pointers
{
}
void YetAnotherFunc2(int& view)  // Noncompliant //S995
{
	auto b = view;
}
void AnotherFunc(int view) // Noncompliant //S994 - but NOT new rule - this scenario is the main case why S994 is not suitable
{
}
void myfunc()
{
	std::string& s = getString(); // Noncompliant //S5350

	auto x = s.size(); // modified
	x++;

	auto y = x; // Noncompliant //S994 - and new rule
	int *z = &x; // Noncompliant - new rule or another one for local pointers?

    std::vector<int> v{ i, i, i };  // const not needed
    return v;
}

external references and/or language specifications

    • C++ code guidelines: Rules Con.1 and Con.4, noting also the exceptions under Con.1
      C++ Core Guidelines
    • A similar topic was raised before regarding S994, but S994 was deemed correct for enforce const for function parameters. This new request is not asking to modify S994, but add an alternative.
  • type : Code Smell

  • Tags: C++ core guidelines

Hi @magicmalcsiress and thanks for your message,

It’s true that some guidelines like C++ Core Guidelines recommend marking as many variables as possible const. After careful discussion, we decided that it is not a rule that we would like to implement or enforce on our side. Here are some reasons why we decided that:

  • The benefit of marking all local variables as const is very limited in most functions that should only have a few lines of code and few variables introduced.
  • Terse code is more readable and adding const everywhere creates a (small) additional cognitive load when reading the code.
  • As mentioned by C++ Core Guidelines, making some variables const will prevent copy-elision by the compiler. So const should not be used in those cases. The rules on copy-elision are not easy (they depend on the version of the language and the compiler is allowed to do more copy-elision than what the standard mandates) so that means that “adding const everywhere the variable is not changed” is in fact “adding const whenever a variable is not changed and it doesn’t reduce performances”, which is a lot more complex and puts a serious cognitive burden on the person writing the code. Moreover, the absence of const no longer clearly means that a variable is modified later, but potentially that this variable is a candidate for copy elision.
  • Having const on all local variables also has a small cost for maintenance and might sometime push a maintainer to do a copy of a const variable when the best solution would have been to remove the const and change the variable in place.

All these costs are clearly very small, but because the benefit is very small too, we think they cancel each other out.

We also note that MISRA C++23 removed completely this rule from its guideline, which makes us think that that recommendation is losing traction in modern C++.

Hi there.

Thanks for your consideration.

Just to clarify, this is just asking for local value variables to be const, where they are not modified.
Also not asking for this to be the default, but an option some can choose.

MISRA had parameters as const as well, which is not what we want, and regard this as going overboard, especially as it relates to function signature.
So we were wanting something different.

C++11 and later increasingly allow more things to be returned from a function compared to out reference parameters (move semantics, tuples, structured binding, optional, variant) so we find there is greater number of cases where variables are set on initialisation then never changed, where previously it wasn’t possible.
And this provides better readability/understandability in see what variables are changed vs not.

We often ask on code reviews for variables to be made const, if they weren’t, and so this slows down our development process. As people can run sonarqube localy, this would be a greate benefit for us.