4.14 — 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 gravity of Earth (near the surface): 9.8 meters/second^2. This isn’t likely to change any time soon (and if it does, you’ve likely got bigger problems than learning C++). 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 const 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 other variables (including non-const ones):

Const is 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.

When arguments are passed by value, 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.

Runtime vs compile time constants

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 in the snippets above are runtime constants, because the compiler can’t determine their initial 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). However, once initialized, the value of these constants can’t be changed.

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. Compile-time constants enable the compiler to perform optimizations that aren’t available with runtime constants. For example, whenever gravity is used, the compiler can simply substitute the identifier gravity with the literal double 9.8.

When you declare a const variable, the compiler will implicitly keep track of whether it’s a runtime or compile-time constant.

In most cases, this doesn’t matter, but there are a few odd cases where C++ requires a compile-time constant instead of a run-time constant, for example in the instantiation of type -- something we’ll cover later.


To help provide more specificity, C++11 introduced the keyword constexpr, which ensures that a constant must be a compile-time constant:

constexpr variables are const. This will get important when we talk about other effects of const in upcoming lessons.

Best practice

Any variable that should not be modifiable after initialization and whose initializer is known at compile-time should be declared as constexpr.
Any variable that should not be modifiable after initialization and whose initializer is not known at compile-time should be declared as const.

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 4.13 -- 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 2.9 -- Introduction to 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). That way, if we need to update our classroom size, we won’t accidentally change the name length too.

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

First, because macros are resolved by the preprocessor, the all instances of the macro are replaced with the defined value just prior to compilation. If you are debugging your code, you won’t see the actual value (e.g. 30) -- you’ll only see the name of the symbolic constant (e.g. MAX_STUDENTS_PER_CLASS). And because these #defined values aren’t variables, you can’t add a watch in the debugger to see their values. If you want to know what value MAX_STUDENTS_PER_CLASS resolves to, you’ll have to find the definition of MAX_STUDENTS_PER_CLASS (which could be in a different file). This can make your programs harder to debug.

Second, macros can conflict with normal code. For example:

If someheader.h happened to #define a macro named beta, this simple program would break, as the preprocessor would replace the int variable beta’s name with whatever the macro’s value was.

Thirdly, macros don’t follow normal scoping rules, which means in rare cases a macro defined in one part of a program can conflict with code written in another part of the program that it wasn’t supposed to interact with.


Avoid using #define to create symbolic constants macros.

A better solution: Use constexpr variables

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

Because these are just normal variables, they are watchable in the debugger, have normal scoping, and avoid other weird behaviors.

Best practice

Use constexpr variables to provide a name and context for your magic numbers.

Using symbolic constants throughout a multi-file 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 6.2 -- User-defined namespaces)
3) Add all your constants inside the namespace (make sure they’re constexpr in C++11/14, or inline constexpr in C++17 or newer)
4) #include the header file wherever you need it

For example:

constants.h (C++11/14):

In C++17, prefer “inline constexpr” instead:

constants.h (C++17 or newer):

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.

4.x -- Chapter 4 summary and quiz
4.13 -- Literals

289 comments to 4.14 — Const, constexpr, and symbolic constants

  • even g is constant variable, g always takes the value from the function gravity(23) and print out 23 why?
    #include <iostream>

    void gravity(const int g = 9.9) {
        std::cout << "The gravity is : " << g << '\n';

    int main() {
        return 0;

    • Alex

      A const parameter means the value can't be changed after initialization.

      If the user passes an explicit value for a parameter that has a default argument, the user-provided argument takes precedence, and is used for initialization.

  • Haldhar Patel

    "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"

    > I have tried


    but they both says

    in the debugger, so what's the difference here?

    • Alex

      The macro's name shows up in the debugger, but not the value that it resolves to.

      Consider this program:

      In Visual Studio, stepping into this program, when I hover MAX_STUDENTS_PER_CLASS I get no hover information. When I hover maxStudentsPerClass, it tells me the value (30).

  • Soyazhe

    I have tried defining a namespace in a header file for all my program constants. Visual Studio, for some reason, does not like that I am putting "inline" with my definitions. This header would be included in multiple source files, so I want to minimize overhead.

  • James C

    I think there's an error in this paragraph:

    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 };, and MAX_STUDENTS_PER_CLASS would not be watchable. 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.

    It says #defined constants don't show in the debugger, but then it says in the debugger it will show MAX_STUDENTS_PER_CLASS

  • Hi

    It might be helpful to note that the "Using symbolic constants throughout a multi-file program" section only applies to const and constexpr's.

    (I spent an hour trying to figure out why one of my test programs doesn't work and the reason was because it wasn't constexpr)

  • noman

    constants are not variables.  A couple of times you say constant variable.  A variable varies (its value changes).  A constant does not vary. I understand this is common usage, sigh.

    another issue of #define is they are typeless.  Its not normally a big deal but you can have ints being put into longs, floats into doubles, etc.  Const and {} and constexpr help with strong typing.

    • Corrado

      As a beginner makes it a lot of sense calling it constant variable even if this is an oxymoron, cause you use the same procedure for defining a variable and adding const before. If you remove const what you have is a variable. With time the programmer will call it only constant.

    • Corrado

      Also remember that mutable class specifier allow const object to be modified.

  • xhq

    This paragraph seems to be mistaken - 30 is what would show up in the debugger and MAX_STUDENTS_PER_CLASS  would be what the compiler would compile:

    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 };,

  • hali code


    inline constexpr type variable{expression};

    `inline is c++17 or higher`, that bring me into questions, why is there a different version of c++? do you mean a compiler? a different type of compiler? what's the different between them and which is better than the other or prefer better one than the other?

    what is inline variable mea?

  • Sahil

    Hi, thanks for such a great tutorial.

    Can you explain the difference between constexpr and inline constexpr? I tried looking it up else where but there it said that constexpr explicitly evaluates at compile-time where as inline constexpr evaluates at run-time but won't that make it just like a normal const??

    • nascardriver

      `constexpr` means that the variable _can_ be computed at compile-time. The compiler is free to choose when the computation takes place, no matter if an variable is `constexpr` or not.

      `inline constexpr` gives the variable external linkage, but allows it to be defined multiple times. This allows placing the variable's definition in a header file and including it in multiple source files without violating the one-definition-rule.
      Placing a non-`inline` `constexpr` variable in a header would work too, because `constexpr` variables have internal linkage, but the variable would be defined, and therefore take up memory, for each source file it's included in. If you include the header in 20 source file, you're potentially taking up 20 times the amount of memory you'd actually need.

      • Sahil

        This helps a lot. Thanks once again.

      • Forhad Rahman

        "This allows placing the variable's definition in a header file and including it in multiple source files without violating the one-definition-rule."

        Soo... if I use 'inline', it'll virtually(everything is virtual though :v) place a copy of my code in multiple source file, without taking any extra space for those copy?? It's seems a bit wired how the compiler does it without taking that extra memory.

        Doesn't using 'inline' increase the compilation time?

        Thanks if Advance <3

        • nascardriver

          > without taking any extra space for those copy?
          If the function/variable actually gets inlined (copied to the caller), you can end up using much more space.

          > Doesn't using 'inline' increase the compilation time?
          Anything you do in a header can drastically increase compilation time, because the contents get compiled as part of every source file that includes the header.

          • Haldhar Patel

            "If the function/variable actually gets inlined"
            >What do you mean by actually getting inlined?

            > So here you are saying that inline variables take up memory if they are included and also increase the compilation time then how they are more efficient than normal constexpr variables?
            I am confused after your wording here.

            • nascardriver

              An `inline` entity may or may not get inlined by the compiler. An `inline` variable can get inlined by the compiler, but it can also be stored like a normal variable.

              Say you have a header containing this variable

              Without `inline`, `helloworld` we might created in every source file that includes it. You can end up with having N instances of `helloworld`, each taking up 1000*sizeof(int) bytes.

              With `inline`, a single `helloworld` instance can be shared between all source files (As if it was `extern` and defined in a source file).

              • Haldhar Patel

                Thanks for the quick response nascardriver!
                You cleared my doubt here .
                But I needed further clarification on compilation , correct me if I am wrong here:
                >if a header contains declaration(not definition) of some extern functions or variables and the header is included in multiple sources and if we compile our project then  the compiler will be compiling only the definitions of extern variable or function in the source file related to the header but not all the sources which contain the header as they will only contain the declarations right?

                But if the header contains inline variables or functions(definitions) and the compiler chooses not to inline and hence normal variables will be included in all the headers and hence in all sources which include them(that's why they take up memory now) , and if we compile now then it needs to compile every definition of the header in each source file and hence increase compilation time right?
                So we have to make functions or variables inline with a caution as the request might be ignored by the compiler right?

                • nascardriver

                  No matter if declaration or definition, or inline or not, anything in a header is compiled as part of every source file that includes the header.

                  The compiler doesn't know what it previously did with other files or what it will do in the future.
                  It will compile, and write to the output file, every `inline` function from every included header. If the functions weren't inlined by the compiler, the linker will remove all but 1 of them when it merges the output files into a single binary.

  • Frido Kurniawan

    Hello there

    just wanna give you little suggestion
    according to your suggestion in the best practice section, we should avoid magic number altogether.
    But i still found magic number in your last code snippet here :

    so how about we replace "radius" to "diameter" instead? In order to discard magic number 2.0 to calculate diameter.
    Like this :

    Or perhaps we could keep the "radius", but also create new particular function just to calculate the diameter?

    Thanks Before... Great tutorial by the way

    • Haldhar Patel

      We know that circumference of a circle = 2*pi*r
      hence writing 2.0 * radius * constants::pi seems much more intuitive than diameter * constants::pi.

  • J34NP3T3R


    1.) if we must not use #define then can #ifndef identify constant variables created by constexp ?
    and can we then replace #define with constexp ?

    #ifndef CONSTANTS_H
    #define CONSTANTS_H   --->>       constexpr (string or something) CONSTANTS_H { };   ?

    2.) what does inline before constexpr do ?

    • Sahil

      I'm not sure about what inline does but for your first question, the
      #ifndef    and
      are used so that when you include you self created header files into your program file the preprocessor doesn't end up copying the same code (from the header) into your main file twice by mistake (if more than 1 file used #include "your_header" ) as copying the same code twice can lead to compiler errors.

      basically the macro that you are talking about is never going to be used in the code. It is there for the preprocessor.

      I tried my best to explain but its probably not clear, so you should check out lesson  2.11- Header Guards

  • Tony

    Hey nascar & Alex, could you please explain the use of constexpr in functions?

    I don't understand when and why you should use it.
    For example in the standard library (std::array):

    Why is constexpr used here? When should I use it, and how do I know whether the function actually needs it at all?

    Thank you :) Nice to see you again btw!

    • nascardriver

      The more `constexpr` the better, usually. There are some downsides to it so you have to decide for yourself.
      `constexpr` functions can be evaluated at compile-time. You can create a `constexpr` `std::array` and access its elements at compile-time.

      The compiler knows the values of the elements, so the compiler can figure out the value of `i` at compile-time. The array and `i` disappear during compilation, the program is identical to

      because the compiler was able to do everything apart from printing at compile-time.

      Functions can only be marked as `constexpr` if they really can be evaluated at compile-time. A function that prints something to `std::cout` cannot be `constexpr` for example.

  • kiwiz

    so basically a symbolic constant is the same as a constant variable?
    oops sorry ignore this comment,i reread again and understood it. Side note, if i remembered correctly, last time able to delete comments

  • TonyCheeze

    Hi! So when you say that the compiler doesn't know what value is passed to a function, what exactly does that mean? You stated that "myValue depends on the value passed into the function (which is only known at runtime)."
    But, when the compiler is parsing the document, does it not read the line that contains the caller, and can it not determine the type of the value. For example, in this snippet of code:

    I get an error: error: cannot convert 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'int'. So, I don't understand how the values passed into functions are not known until runtime. I don't believe this was elaborated on further in any other sections. Could you clarify a bit? Thanks!

    • nascardriver

      By default, functions and variables are assumed to be evaluated at run-time. The compiler reads their definition, also reads their value (If initialized with a literal), but it doesn't need to keep track of the value, that's the task of the run-time.

      The compiler knows `doSomething` will be called with the value stored in `a`, but it doesn't need to know which value `a` has. The compiler writes "Write 123 to variable `a`" and "look into `a` and pass its value to `doSomething`" into the binary. It doesn't need to know `a`s value when it writes the instruction to call `doSomething`.

  • Waldo Lemmer

    > int age;

    at code block 5, line 2 should be

    > int age{};

    for consistency.

    2. Why don't new projects in Visual Studio use the C++17 compliant compiler by default?

  • Hakan

    Instead of creating variables in namespace with inline & constexpr; using a Helper class/struct to wrap static constexpr variable would be more readable.

  • Thomas

    First of all, thank you for making! It’s a amazing tool for learning C++!

    I’m starting to get quite confused about when it’s ok to use magic numbers it’s not. I’m sorry if my following questions are are badly formulated(english isn’t my first language). Or coming off as ignorant, there is still a lot I don’t know about programming in general and C++ specifically.

    #1 my understanding of what a magic number is:
    I had python at uni but my professor never told us anything about magic numbers, so this sub chapter was the first time I heard about them. As far as I understand is a magic number “A magic number is any number that is not given a name.”  I provide some examples of what would be magic numbers below.

    Based on my understanding would 2, 4, 4, 4, 2 and 0  in Example 1 all be regarded magical numbers. My reasoning for this is that its hard to see witch animal the number relate from the initializations of the array alone.

    In Example 2 are 1 and 100 in line 43 directly used but commented, witch increases the readability of the code but it’s still as far as I understand still magical numbers and should be defined in a variable for increased readability and ease of maintenance.

    Example 1 - From solution of task 2 in the Quiz in chapter 6.2 — Arrays (Part II)

    Example 2 - From  solution to task 2 in 5.x — Chapter 5 comprehensive quiz

    #2 I’m now at 6.3 - Arrays and loops. I’m I right in thinking that the reason that the examples uses so many magical numbers is that the intent of the example is to show how the different concepts works in a as compact form as possible and that’s why you don’t define all the magical numbers?

    #3 Is it any cases where using magical numbers actually is prepared? I see that a lot of people on Quora and in general uses quite a bit of magical numbers. Do you think that this is just for convenience while coding or is it a good reason to do so?

    • nascardriver


      magic numbers are all values that are based on something else in your code or are not obvious from their context. There is no strict definition of magic numbers, so you'll come across different explanations. Comments don't make literals non-magic. Code should be readable without comments.

      Example 1: Not magic. These values are correlated to the animals by index. Although it's not easy to see which animal has which number of legs, the meanings of the numbers is obvious (Number of legs).

      Example 2: Not magic. As in #1, the reader can look up which parameters `std::uniform_int_distribution` takes. If you were to use these values again to print the message in line 50, they'd become magic, because they could change independently but mean the same thing. If you wanted to print those numbers, you should use `die.a()` and `die.b()` or add constants.
      But there's something else in this code. The 'y' in `playAgain` is used more then once, but changing one or the other wouldn't make sense. Clearly, a constant should be used instead. I updated that lesson to use a `while (true)` loop to get rid of the duplicate.

      If a values makes a reader think "Why this value?", it's magic.

      I think it's easiest if you post your quiz solutions in the lessons you're in right now and I'll tell you what's magic and what's not.

  • Keshav

    Missed the #include<iostream> and int main()

  • Keshav

    I am getting an error in this.why?

    the error message is ......

    Error       code:C2678    desription:binary '<<': no operator found which takes a left-hand operand of type 'std::istream' (or there is no acceptable conversion)    project:Testing Subject    file:C:\Users\HP PAVILLION_2\source\repos\Testing Subject\Testing Subject.cpp    line:6    

    here's the code......

    and also I compiled it and works after I close the console and to the error list it still shows the error

  • sami

    "because the compiler can’t determine their initial values at compile time. "

    How come can't the compiler determine those initial values at compile time and STILL won't complain while when we declare an array with size of type a variable "int n {10}; int x[n];" it will complain? Don't compiler have any idea about variables' values during compile time?
    Why can't a compiler know the value of n is 10 "int n{10};" as it is not a user input or a function parameter to be known during run-time ?

    • nascardriver

      Making exceptions is bad. It makes code and compiler implementation a lot more complicated.
      Right now, we say "If a variable is `constexpr`, it's a compile-time constant". Otherwise, it's not. (There is, unfortunately, an exception to this, duh).
      By allowing regular variables to be used in a constant context, it'd have to be specified under which condition it's allowed, so you'd end up with something like "If a variable is `constexpr`, or it is only used in a read-only manner before it is used in a constant context and it is initialized with a value or variable that is usable at compile-time, it's a compile-time constant".
      Needless to say, the first version is a lot simpler. People (and the compiler) don't have to read your code to know if a variable is usable at compile-time. It's only usable at compile-time if you say so (Plus the exception I mentioned before, but won't go into detail).

  • Dean

    I believe the nomenclature of calling a constant a 'constant variable' as used in this lesson is incorrect. A variable is called as such because it can vary. A constant cannot. A constant and a variable are distinctly different and opposite.

  • Jingtong

    For the example of creating a namespace constants, I got an error when I defined a function with the same name constants. So anyone knows about namespace and function name conflict?

  • Alek

    Hello,I've several questions, would be thankful if you could answer these:
    1:in the part you are talking about "making a function parameter const" you say: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. aren't these two reasoning the same ?

    2:before you talked about constexpr you provided an example which you are using std::bitset in a form.can I ask what it is and what does this piece of code do "std::bitset<numberOfBits> b{};"

    3:can you please provide an example for the third major problem of using object-like macros to define symbolic constants which says: Thirdly, macros don’t follow normal scoping rules, which means in rare cases a macro defined in one part of a program can conflict with code written in another part of the program that it wasn’t supposed to interact with.

    4:what's the difference between inline-constexpr & constexpr.

    thanks & sorry, I'm bombarding you with my questions lately.

    • nascardriver

      > it tells the person calling the function that the function will not change the value of myValue
      This doesn't make sense yet. When you get to references, you'll understand the difference.

      Seems like I mixed up some lessons. `std::bitset` is covered later, I removed the example from this lesson.


      `inline` variables can have multiple identical definitions, but only 1 variable will actually be created. Without `inline`, you'd create a variable (Which uses up memory) for each definition. For non-const variables, `inline` can also be used to bypass the one-definition-rule.

  • als


  • Chayim

    For what is it called "symbolic"? It’s a "define" function.

  • Chayim

    Function -numClassrooms- was not declared.

  • Chayim

    How do I post a code snippet on here? I tried to code inside [] but it posts it as text.

  • Bolsonaro

    Can constexpr be declared inside namespaces and forward declared?




    C:\Users\admin\Documents\cbp\test\constants.h|6|error: uninitialized const 'myconstants::myconst' [-fpermissive]|

Leave a Comment

Put all code inside code tags: [code]your code here[/code]