Search

2.9 — Const, constexpr, and symbolic constants

Const variables

So far, all of the variables we’ve seen have been non-constant -- that is, their values can be changed at any time. For example:

However, it’s sometimes useful to define variables with values that can not be changed. For example, consider the value of gravity on Earth: 9.8 meters/second^2. This isn’t likely to change any time soon. Defining this value as a constant helps ensure that this value isn’t accidentally changed.

To make a variable constant, simply put the const keyword either before or after the variable type, like so:

Although C++ will accept const either before or after the type, we recommend using it before the type because it better follows standard English language convention where modifiers come before the object being modified (e.g. a green ball, not a ball green).

Const variables must be initialized when you define them, and then that value can not be changed via assignment.

Declaring a variable as const prevents us from inadvertently changing its value:

Defining a const variable without initializing it will also cause a compile error:

Note that const variables can be initialized from non-const values:

Const is used most often with function parameters:

Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

With parameters passed by value, like the above, we generally don’t care if the function changes the value of the parameter (since it’s just a copy that will be destroyed at the end of the function anyway). For this reason, we usually don’t const parameters passed by value. But later on, we’ll talk about other kinds of function parameters (where changing the value of the parameter will change the value of the argument passed in). For these types of parameters, judicious use of const is important.

Compile time vs runtime

When you are in the process of compiling your program, that is called compile time. During compile time, the compiler ensures your code is syntactically correct, and converts your code into object files.

When you are in the process of running your application, that is called runtime. During runtime, your program executes line by line.

Constexpr

C++ actually has two different kinds of constants.

Runtime constants are those whose initialization values can only be resolved at runtime (when your program is running). Variables such as usersAge and myValue above are runtime constants, because the compiler can’t determine their values at compile time. usersAge relies on user input (which can only be given at runtime) and myValue depends on the value passed into the function (which is only known at runtime).

Compile-time constants are those whose initialization values can be resolved at compile-time (when your program is compiling). Variable gravity above is an example of a compile-time constant. Whenever gravity is used, the compiler can simply substitute the identifier gravity for the literal double 9.8.

In most cases, it doesn’t matter whether a constant value is runtime or compile-time. However, there are a few odd cases where C++ requires a compile-time constant instead of a run-time constant (such as when defining the length of a fixed-size array -- we’ll cover this later). Because a const value could be either runtime or compile-time, the compiler has to keep track of which kind of constant it is.

To help disambiguate this situation, C++11 introduced new keyword constexpr, which ensures that the constant must be a compile-time constant:

Most of the time you won’t need to use constexpr, but it’s there if you need it.

Rule: Any variable that should not change values after initialization should be declared as const (or constexpr).

Naming your const variables

Some programmers prefer to use all upper-case names for const variables. Others use normal variable names with a ‘k’ prefix. However, we will use normal variable naming conventions, which is more common. Const variables act exactly like normal variables in every case except that they can not be assigned to, so there’s no particular reason they need to be denoted as special.

Symbolic constants

In the previous lesson 2.8 -- Literals, we discussed “magic numbers”, which are literals used in a program to represent a constant value. Since magic numbers are bad, what should you do instead? The answer is: use symbolic constants! A symbolic constant is a name given to a constant literal value. There are two ways to declare symbolic constants in C++. One of them is good, and one of them is not. We’ll show you both.

Bad: Using object-like macros with a substitution parameter as symbolic constants

We’re going to show you the less desirable way to define a symbolic constant first. This method was commonly used in a lot of older code, so you may still see it.

In lesson 1.10 -- A first look at the preprocessor, you learned that object-like macros have two forms -- one that doesn’t take a substitution parameter (generally used for conditional compilation), and one that does have a substitution parameter. We’ll talk about the case with the substitution parameter here. That takes the form:

#define identifier substitution_text

Whenever the preprocessor encounters this directive, any further occurrence of ‘identifier’ is replaced by ‘substitution_text’. The identifier is traditionally typed in all capital letters, using underscores to represent spaces.

Consider the following snippet:

When you compile your code, the preprocessor replaces all instances of MAX_STUDENTS_PER_CLASS with the literal value 30, which is then compiled into your executable.

You’ll likely agree that this is much more intuitive than using a magic number for a couple of reasons. MAX_STUDENTS_PER_CLASS provides context for what the program is trying to do, even without a comment. Second, if the number of max students per classroom changes, we only need to change the value of MAX_STUDENTS_PER_CLASS in one place, and all instances of MAX_STUDENTS_PER_CLASS will be replaced by the new literal value at the next compilation.

Consider our second example, using #define symbolic constants:

In this case, it’s clear that MAX_STUDENTS_PER_CLASS and MAX_NAME_LENGTH are intended to be independent values, even though they happen to share the same value (30).

So why not use #define to make symbolic constants? There are (at least) two major problems.

First, because macros are resolved by the preprocessor, which replaces the symbolic name with the defined value, #defined symbolic constants do not show up in the debugger (which shows you your actual code). So although the compiler would compile int max_students = numClassrooms * 30;, in the debugger you’d see int max_students = numClassrooms * MAX_STUDENTS_PER_CLASS;. You’d have to go find the definition of MAX_STUDENTS_PER_CLASS in order to know what the actual value was. This can make your programs harder to debug.

Second, #defined values always have file scope (which we’ll talk more about in the section on local and global variables). This means a value #defined in one piece of code may have a naming conflict with a value #defined with the same file later.

For example:

In the above code, we #define x in function a(), intending it to be used inside function a(). However, #define value x can actually be used anywhere beyond that point in the same file. So when function b() #defines its own x, we get a naming conflict. Even if function b() used a variable named x instead of a #define value named x, we would still have issues, as the preprocessor would try to replace variable name x with the value 5.

Rule: Avoid using #define to create symbolic constants

A better solution: Use const variables

A better way to create symbolic constants is through use of const variables:

These will show up in the debugger, and follow all of the normal variable rules around scope.

Rule: use const variables to provide a name and context for your magic numbers.

Using symbolic constants throughout a program

In many applications, a given symbolic constant needs to be used throughout your code (not just in one location). These can include physics or mathematical constants that don’t change (e.g. pi or avogadro’s number), or application-specific “tuning” values (e.g. friction or gravity coefficients). Instead of redefining these every time they are needed, it’s better to declare them once in a central location and use them wherever needed. That way, if you ever need to change them, you only need to change them in one place.

There are multiple ways to facilitate this within C++, but the following is probably easiest:

1) Create a header file to hold these constants
2) Inside this header file, declare a namespace (we’ll talk more about this in lesson 4.3b -- Namespaces)
3) Add all your constants inside the namespace (make sure they’re const)
4) #include the header file wherever you need it

e.g. constants.h:

Use the scope resolution operator (::) to access your constants in .cpp files:

If you have both physics constants and per-application tuning values, you may opt to use two sets of files -- one for the physics values that will never change, and one for your per-program tuning values that are specific to your program. That way you can reuse the physics values in any program.

Note: In lesson 4.2 -- Global variables and linkage, we show a more efficient way to do symbolic constants using global variables.

2.10 -- Chapter 2 comprehensive quiz
Index
2.8 -- Literals

107 comments to 2.9 — Const, constexpr, and symbolic constants

  • himanshu

    Hey alix
    i want to know something
    IS this site created with c++

  • Marius

    Hello, Alex!

    Typo:

    The line over "Compile time vs runtime":

    "For theses types of parameters, judicious use of const is important."

    "theses" should be "these" I believe 🙂

  • my_gravity(9.2) is wrong. According to the theory, value of g = 9.8 m/s^2. So please provide the correct constant values for universal constants.

  • Hema

    A non-constant variable can be converted into a contant varialbe. Can the reverse be possible?

    • Alex

      Yes, you can use a const_cast to get rid of a const, but:
      1) You shouldn’t do this.
      2) It’s only reliable if the original object was non-const in the first place.
      3) You shouldn’t do this.

  • Simon

    Const is most useful (and most often used) with function parameters:

    Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

    I think this is misleading. As a caller of the function printInteger I don’t care if the (copied value of) myValue is being changed within the function’s body. From the caller’s perspective, there is no difference in behavior between the prototype with and without ‘const’.

    It’s different if the argument is passed by reference, though.

  • gary wang

    Hi, Alex. I have a small question about the symbolic constant.
    As you said, there are two ways to do the symbolic constant. First is by using #defiine and the second one is just use const keyword.

    My question is, I am sure that the second one would take up memory for the program, but what about the first one by using #define. does it take the memory to store the symbolic constant? thank you!~

    • Alex

      In theory, yes, but two thoughts here:
      1) You shouldn’t be optimizing to save a few bytes.
      2) The compiler would probably optimize away the variable anyway.

      Even if it doesn’t, you should still use the const version. It’s easier to debug.

      • Alex

        I’m re-learning C++ after over 20 years working with higher level languages. These tutorials have been invaluable. Thank you.

        There is a popular use case you might want to consider. Internet of things and wearable technology. Things like the Arduino and ESP range of processors.

        The ESP8266, for example, has only about 80kBytes of RAM available for variable storage.

        It is therefore very important to minimise the scope of any variable. In some cases global variables are desirable but global constants are a significant waste of precious RAM.

        I use #define replacement values at the start of my code to make it easier to tweak while still avoiding ‘magic numbers.’ They can still be assigned to an appropriately named variable but only within the scope they are needed.

        Is that an acceptable way to do that or is there a better way?

        Thank you.

  • Shashank

    Hi,

    If I call a() in b(), shouldn’t the output be 555 instead of 556? Once a() is called inside b(), shouldn’t x start getting substituted by 5 again from this point onward.

    [void b()
    {
        // Even though we’re intending this x to be local to function b()
        // it conflicts with the x we defined inside function a()
    #define x 6
        a();
        std::cout << x;
    }]

  • Rohan

    Respected Sir,
    I have a doubt. In previous section you mentioned that multiple definition raise an error. Please look at the below 2 sets of code both compiled individually. Second one runs just fine but the first doesn’t. I was wondering why is that?

    Code1:

    square.h:

    #ifndef SQUARE_H
    #define SQUARE_H

    int getSquareSides()
    {
        return 4;
    }

    int getSquarePerimeter(int sideLength);

    #endif

    square.cpp:

    #include "square.h"

    int getSquarePerimeter(int sideLength)
    {
        return sideLength * getSquareSides();
    }

    main.cpp:

    #include <iostream>
    #include "square.h"

    int main()
    {
        std::cout << "a square has " << getSquareSides() << " sides" << std::endl;
        std::cout << "a square of length 5 has perimeter length " << getSquarePerimeter(5) << std::endl;

        return 0;
    }

    Code 2:

    constants.h:

    #ifndef CONSTANTS_H
    #define CONSTANTS_H

    void print();

    namespace constants
    {
        const double pi(3.14159);
        const double avogadro(6.0221413e23);
        const double my_gravity(9.2);
    }

    #endif

    Function.cpp

    #include <iostream>
    #include "constant.h"

    void print()
    {  
        std::cout << "Printing";
    }

    main.cpp

    #include <iostream>
    #include "constant.h"

    int main()
    {
        print();
        return 0;
    }

    According to what I have understood the first code is not working because of multiple definitions of ‘getSquaresides’. But in second code similarly there are multiple definitions of variable inside constant namespace but still the code works fine. If in the first code if I modify the header file by removing the definition of ‘getSquaresides’ and keeping only declaration and adding the definitions of different variables then again the program runs fine (After defining ‘getSquaresides’ in some other file). Why is this happening? I have even confirmed that multiple definitions of neither functions nor variables are allowed. Then why just by replacing function by variable the code is working? I am really very confused. I will be obliged to receive any response that will take me out of this confusing situation. Thank you.

    • Alex

      Good question. The short answer is that functions (by default) have external linkage, which means a function defined in one file can be accessed in another file (this is why function forward declarations work). It also means if a function is defined in more than one file, those definitions will conflict. In contrast, const variables (by default) have internal linkage, meaning they’re only visible in the file in which they are defined. That means that they won’t conflict with variables of the same name in other files.

      This is discussed in more detail at the start of chapter 4.

      • MSteven

        Hi Alex How getSquareSides() function is defined multiple times in these files?

        • Alex

          #including a header simply copies the header file into the including file at the point of inclusion. Since square.h includes a definition for getSquareSides(), that definition is copied into any file that #includes square.h. In this program, two files include square.h: main.cpp and square.cpp. So two files end up with a definition for getSquareSides(), and that causes issues.

          • MSteven

            You stated <a href="http://www.learncpp.com/cpp-tutorial/1-10a-header-guards/">here</a> that:
            <i>Note that even though square.h has header guards, the contents of square.h are included once in square.cpp and once in main.cpp.

            Let’s examine why this happens in more detail. When square.h is included from square.cpp, SQUARE_H is defined until the end of square.cpp. This define prevents square.h from being included into square.cpp a second time (which is the point of header guards). However, once square.cpp is finished, SQUARE_H is no longer considered defined. This means that when the preprocessor runs on main.cpp, SQUARE_H is not initially defined in main.cpp.

            The end result is that both square.cpp and main.cpp get a copy of the definition of getSquareSides(). This program will compile, but the linker will complain about your program having multiple definitions for identifier getSquareSides!</i>

            In my computer It’s at compile time that i have an error on multiple definitions and I’d thought that even though square.h has header guards, the contents of square.h are included once in square.cpp and once in main.cpp like you mentionned in 1.10 section.

    • Rohan

      Thank you! it has helped a lot.

  • Sean Kelly

    When it comes to symbolic constants would you say the enumerators are a good way to work with them as well?
    This just came to mind when I was I had to look back as to why #define is not the best way to create
    symbolic constants.

  • Kate

    Const is most useful (and most often used) with function parameters:

    Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

    That was from the tutorial. I don’t understand how when using const in a parameter how you will later be able to use it an argument? Does this imply that the value of myValue was declared in main? Thank you so much for this wonderful tutorial.

    • Alex

      Not sure I understand your concern. You can pass a non-const argument to a function with a const parameter, and the function will treat the parameter as const. This doesn’t affect the argument.

      For example:

  • Alan

    Hi, Alex
    First, thank you for the answer to my question asked in section 2.3 - Variable sizes and the sizeof operator two days ago.
    After I tried

    on my Code::Blocks, it showed a warning that stated "x" being redefined, no errors. As I run this program, it worked fine and it printed "56". I got two questions for this issue.

    1. Can the constants defined via object-like macros with substitution parameter be (inadvertently) changed in this way or is it just another example that some compilers don’t follow the rule of standard C++?

    2. Suppose that the answer to the previous question is a yes and some compilers DO do (awkward) things like that. I remembered that in section 1.10 - A first look at the preprocessor, you said, "The preprocessor is perhaps best thought of as a separate program that runs just before the compiler when you compile your program. When the preprocessor runs, it simply scans through each code file from top to bottom, looking for directives." If I understand correctly, this states that before compilation, the computer (or something else that execute the preprocessor) scans all the preprocessor in the file while ignoring the actual codes. As a result, by the time both of them in this program are scanned, x shall be 6, for the second one says so. However the program printed out a 5 and a 6. Is there anything that I misunderstand?

    • Alex

      1) I think this is a case where your preprocessor is being permissive. You can undefine a macro value and then redefine it -- your preprocessor is probably assuming you wanted to do this and acting on your behalf.
      2) The preprocessor is just a text editor. So first it sees that you’ve defined x as 5. Then it encounters x in the next statement, and literally replaces x with ‘5’. Then later it sees that you’ve redefined x to 6. So when it encounters the next x, it literally replaces x with 6.

      Therefore, when the compiler compiles your code, it’s compiling code that prints 5 in the first case, and 6 in the second case.

  • Charles

    Thanks for your website…nice job explaining C++.  But I do have a question…  In this section (2.9) you state:

    "Second, #defined values always have global scope (which we’ll talk more about in the section on local and global variables). This means a value #defined in one piece of code may have a naming conflict with a value #defined with the same name in another piece of code."

    Notice in particular the "GLOBAL SCOPE" you mention.

    But in section 1.10 you state:

    "The scope of defines

    Directives are resolved before compilation, from top to bottom on a file-by-file basis. Once the preprocessor has finished, all directives from that file are discarded.

    This means that directives are only valid from the point of definition to the end of the file in which they are defined. Directives defined in one code file do not have impact on other code files in the same project."

    This seems to be saying that they are LOCAL in scope…I am confused by this, so if you could clarify please do.

    Thanks!

    • Alex

      Good question -- this is partly due to ambiguity around the term “global scope” and partly due to poor wording on my part. It’s probably more intuitive to say #define values have file scope (they can be seen until the end of the file). When I said “another piece of code”, I didn’t mean another file, I meant code later in the same file.

      I’ve updated the lesson, clarified the wording, and added an example of how a conflict may occur within the same file. Hopefully this will make it clearer.

      Thanks for pointing this out!

  • Jarred

    Earlier in these lessons, weren’t they saying to never define anything inside of a header file because it would make the program excessively large due to how the preprocessor functions? But, if that is the case, why did they define the constants, instead of having a forward declaration to define them in some other location?

    • Alex

      A few reasons:
      1) To keep the examples simple
      2) We haven’t covered some of the basics needed to do this effectively. We talk more about this in chapter 4, once we’ve covered global variables.

      • Jarred

        So, to clarify, it really still is bad practice to do that, you just have to do that at this point because it would be too much to explain at this point?

        • Alex

          No, not bad practice. Just in doing so, you need to be aware of the tradeoffs. If you put your constant definitions in a header file, they will get recompiled into every code file the header is included from. That can lead to code bloat if there are a lot of constants and/or those constants are large. But assuming that’s not the case, putting them in the header is easier, and sufficient.

  • maria

    in the code for calculating the circumference of a circle……what do i include in the main.cpp file? i mean mean the 2nd part of the code goes to the constant.cpp  file if i am not mistaken..   …..once again sorry for my stupid question new to programming

    Talking about this part of code….

    • Alex

      I’m not sure I understand your question. The constants you need are in constants.h, so by doing #include “constants.h”, you’re bringing those constants into your main.cpp file for use. That allows you to use constants::pi.

      • Maria

        But whenever I create a class file it automatically creates 3 files…main.cpp constant.h. And constant.cpp

        • Alex

          I’m not sure I understand what you mean by “class file”.

          If you’re asking me whether you need a main.cpp if you have a constant.cpp, the answer is no -- you really only need a main.cpp if you want to have one (and if it makes more sense to put your main function there than somewhere else).

  • TripleJski

    "Const variables act exactly like normal variables in every case except that they can not be assigned to, so there’s no particular reason they need to be denoted as special."  When I read this line, I can’t help but think something’s incomplete or a typo.  If not, please explain in detail if you could the thought process, because I’m just not understanding this.  As an aside, love the site, and I’m enjoying learning from you, keep up the good work Alex!

  • AmnesiaMaster28

    There’s a typo that’s bothering me.
    "When you are in the process of running your application, that is called runtime. During runtime, your program executes line by "
    I think you forgot a word there. 😛

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter