10

This question is inspired by a question about final in java.

In C/C++, should I use const whenever possible?

I know there is already a related question about using const in parameters. Unfortunately that question and it's answers don't fully answer my question, because it's only about function parameters, but I would also like to know about other cases (eg: local variables).

Also, almost all answers to that question say we should use const because it contains useful information about the accessibility of variables. But this seems to conflict with an answer about using final in Java which states final may be superfluous if it doesn't contain extra information and so it should be omitted to keep the code short and clean.

So, should I use const whenever possible? If so, why is the advice for const in C++ different from the advice for final in Java?

17

First of all, since you referenced Java's final, that is a totally different beast than const. Final means the reference cannot change, but says nothing about mutability. Const goes further by saying "a const reference cannot mutate" which is a much stronger guarantee. To do this in Java, internal state must be final and determined at construction time. Const is a lot easier to use, and an existing object can be "promoted" into a const reference.

Yes, you should use const whenever possible. It makes a contract that your code will not change something. Remember, a non-const variable can be passed in to a function that accepts a const parameter. You can always add const, but not take it away (not without a const cast which is a really bad idea).

Const-correctness may be tedious at times, but it helps guarantee immutability. This is crucial in multi-threaded code where threads share objects. It makes certain tasks more efficient: instead of copying state, just reuse the same immutable object. Libraries might accept const parameters in order to provide a guarantee to the programmer that no, your object will not change in an unpredictable way in the black hole that is the library's guts.

  • 1
    Up vote because you explain that final and const do not have the same guarantees. – Bill Door Oct 5 '16 at 4:31
  • 2
    Constness is about writeability, not about mutability. – Basilevs Oct 5 '16 at 9:45
  • @Basilevs what happens if you write to an object? It mutates. How do you mutate an object? Write to it. – user22815 Oct 5 '16 at 14:04
  • 4
    What happens if you write to a const reference? A compile time error. What happens if you write to the object that is referenced somewhere by const reference via another non-const reference? It mutates. Immutable object can't mutate, hence const refernce does not necessarily reference immutable object. – Basilevs Oct 5 '16 at 15:45
  • @Basilevs your observation only applies with const refs. If an object itself is declared const (not just the reference) then it it is more-or-less immutable (unless someone nefariously casts away the constness). – Matthew James Briggs Nov 8 '16 at 5:36
4

Personally, const takes me very little time to write, and very little time to read, usually much less than it takes to check whether any code mutates the variable, and it actually prevents me from writing bugs (anyone ever incremented their end iterator by accident? Etc)

I also find that being strict about const usage guides me towards better code in other ways, like creating helper functions to do non-trivial initialization.

3

I'm going to disagree with the other posters and recommend you don't use top-level const on local variables and parameters. (References and pointers to const, i.e. const T&, are different, and you should use them for correctness at all times when it's appropriate.)

The advantages of top-level const are:

  • Makes it clear that the local variables are not meant to be mutated.
  • Gives a compiler error if you accidentally do attempt to mutate them.

The disadvantages are:

  • Visual clutter.
  • Cannot be used on "pseudo-const" variables, i.e. variables that are initialized in some manner more complicated than direct construction, but not modified after, e.g. using getline:

    std::string line; // cannot be const
    std::getline(std::cin, line);
    // line should be const from here on out
    
  • Prevents move-out optimization on function call and return:

    std::string f1();
    std::string f2(std::string s);
    std::string f3() {
      const std::string s = f1(); // s is not meant to be modified
      // but I still want the move optimization here:
      const std::string result = f2(std::move(s)); // this doesn't move
      // and I want the compiler to move out here:
      return result; // this will probably RVO, but if not, the move
                     // constructor will not be used due to the const
    }
    

I have worked on a codebase where local const was used a lot, and I found it not helpful at all, yet it filled the code with subtle pessimizations as above. I personally prefer to adopt a general policy of modifying variables as rarely as possible, and making functions small enough that the usage of a variable is obvious, and even if it is modified, the complexity of the function is low enough to make it a non-issue.

  • 2
    Your point about move-from is showing where you are not using the local in a const manner, as you (intend to) modify it with the move – Caleth Oct 25 '16 at 11:54
  • 1
    @Caleth No, that's the point. I don't intend to modify it. I just want to pass the value, or return it, and I want it to be efficient. Conceptually, the modification is just an optimization, which is not meant to be observable (since the variables aren't used afterwards). Practically, yes, there's a modification, since C++ doesn't have true destructive moves (like Rust has). That's exactly why I don't want const on locals: because it prevents technical modifications, not just conceptual modifications. – Sebastian Redl Oct 25 '16 at 13:11
  • 2
    You don't want to modify the value, but you do want to modify the variable. You can instead use const& instead, and modify neither – Caleth Oct 25 '16 at 13:57
2

The const keyword should be used for local variables, just like parameters. It's useful because:

  • Easier to read the code. When someone reads the variable declaration, she knows it won't change. One less thing to worry while reading your code.
  • Prevent you accidentally modify the variable
  • No runtime penalty, everything is checked statically. It's like a free lunch, const should take just a second to write, so it's no big deal.
  • It's always a good idea to make your variables/parameters in your multi-threaded applications. Even your application is not multi-threaded, it's still a good habit.
  • You give an opportunity for your C++ compiler to optimize your code.

Note that there's no right/wrong answer here. In your link, the answerer who had the most upvotes argued final shouldn't be used locally. However, the next answerer strongly recommended it.

If your function is simple and trivial, you shouldn't add const because everyone understands your intention. However, if the logic in your function is non-trivial, you should use the keyword. Remember, there is little to lose.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.