The typical behavior for a variable in C++ is that its value can be changed; often the value is known at the time it is written and never needs to be changed again. Two keywords allow you to express that intent to the programmer: const and constexpr.
const is the simpler of the two keywords. If you declare a variable with const, the compiler will flag any attempts to change the value as an error. Values must be initialized when you declare them, and after that the name is only useful in read-only terms.
#include <iostream>
int main()
{
const int maxScore{ 100 };
std::cout << maxScore << '\n';
// maxScore = 200; // Compile error
return 0;
}
Declaring const int maxScore{ 100 }; creates an integer named maxScore and promises that it will contain the value 100 forever. The subsequent print statement just accesses the value at that name. The commented-out line would give you a compiler error, because maxScore is not allowed on the left-hand side of an assignment operation. The program will enforce the immutability at compile time, with no regard to what happens at runtime.
There's no problem initializing a const variable with any kind of expression whose value can be calculated when needed.
#include <iostream>
int main()
{
int userInput{};
// Extract formatted input into the target variable
std::cin >> userInput;
const int limit{ userInput * 2 };
std::cout << limit << '\n';
return 0;
}
This limit can be whatever the user typed at the keyboard, but once it is initialized then its contents become read-only. This means that assignment to the variable is still not allowed, even though the compiler can't determine the value until runtime.
constexpr takes the concept of const and moves it up a level. Variables declared as constexpr must be initialized with a constant expression, something the compiler can evaluate while it is translating your code. Because this value is already known at compile time, the language can allow you to use it in places which are themselves required to know compile-time constant values.
#include <iostream>
int main()
{
// Require this value to be usable at compile time
constexpr int sides{ 6 };
constexpr int total{ sides * 4 };
std::cout << total << '\n';
return 0;
}
Both of the variables, sides and total, are declared as constexpr. The computation sides * 4 itself consists entirely of compile-time constant values, and so it can be calculated and stored in total at compile time. In the running program, no calculation is actually performed to get the value for total; it is already known. This means that the print statement is simply accessing a pre-calculated value.
A variable declared constexpr that cannot be evaluated at compile time is itself a compiler error. This program is ill-formed since the calculation needed to initialize sides cannot be executed while the compiler is running, as std::cin requires that you actually execute the program.
#include <iostream>
int main()
{
int userValue{};
std::cin >> userValue;
// constexpr int bad{ userValue }; // Compile error
const int ok{ userValue };
std::cout << ok << '\n';
return 0;
}
This line giving a compiler error is what you expect. However, the line just below it declaring ok with const is accepted; ok is definitely not a constant expression, but this is irrelevant because const doesn't require this, merely that ok cannot later be modified.
Note that because a constexpr variable is also inherently const, modifying a constexpr variable after initialization is not allowed either. The reverse is not true: a const variable is not automatically constexpr. This makes a difference when a variable needs to be usable in a context which is required to have a constant expression such as the size of a C-style array, a non-type template argument, or in a static_assert. Only a literal or a constexpr is appropriate for such contexts, while a const variable the value of which is not yet known cannot do the job.
Practical outcome: use constexpr wherever possible instead of just const. If a variable can have a value before your program is executed, then there is very rarely a benefit in having the language execute this calculation at runtime; there is also usually a benefit in letting the compiler do the work, if possible, rather than you the programmer, especially if a compile-time constant is what is asked for. Where value determination genuinely cannot take place until runtime, for example limit in the earlier example, then const is still perfectly appropriate to prevent modification.
Exercise: Constants with const and constexprStarter code, hints and solutionOpenClose
Practice: Constants with const and constexpr
Work through the mission, reveal hints only when needed, and compare your result with the model solution.
Your challenge
Use 7 as the input and label the printed limit. Explain why limit may depend on input even though it is const.
Mission checklist
- Run the starter as a baseline, then predict the change requested in Your challenge.
- Make that change in your editor; use the lesson-specific hints if you need help.
- Compile and run your answer, then compare with the worked solution and explain the result.
Starter program
This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.
#include <iostream>
int main()
{
int userInput{};
// Extract formatted input into the target variable
std::cin >> userInput;
const int limit{ userInput * 2 };
std::cout << limit << '\n';
return 0;
}Hints
limit is initialized from userInput after std::cin runs. const prevents later assignment to limit; it does not require userInput to be known at compile time.
Try a different small integer input and double it on paper. Replacing const with constexpr here would not work because the initializer depends on runtime input.
Worked solution
Using 7 as the runtime input initializes the const limit to 14. The labeled output shows that const can be initialized from runtime data; it prevents subsequent assignment, not input-dependent initialization.
#include <iostream>
int main()
{
int userInput{};
// Extract formatted input into the target variable
std::cin >> userInput;
const int limit{ userInput * 2 };
std::cout << "limit = " << limit << '\n';
return 0;
}
Example input
7
Example output
Program output only; your terminal may also echo the input you type.
limit = 14
0 comments
No comments yet. Be the first.