SovranCode
Modern C++ Lesson 08
Course contents50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
Modern C++50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
PREVIOUS LESSONC++ Data Types: int, double and Type Conversion
NEXT LESSONC++ Operators: Arithmetic, Comparison and Logic
C++ foundations · Lesson 08 About 45 min with practice

C++ const, constexpr and auto Explained

Understand C++ const, constexpr and auto with separate mental models for immutability, constant expressions and type deduction. Repair runtime constexpr and integer-division mistakes.

Before you begin

Understand initialized variables, int versus double, and conversions. Compile these complete examples using the editor execution service when available or your local C++20 compiler.

What you will learn

  • Use const to express a stable value.
  • Distinguish runtime const values from constant expressions.
  • Use constexpr for suitable compile-time constants.
  • Predict auto from its initializer rather than its name.
  • Combine const or constexpr with deduction intentionally.

Three keywords answer three different questions

const, constexpr and auto often appear in the same declaration, but they do not mean the same thing. Ask whether an object should change, whether its initializer can satisfy a constant-expression requirement, and how its type is determined. const addresses modification; constexpr imposes constant-expression requirements for the variables introduced here; auto asks the compiler to deduce a type. Keeping those questions separate prevents a short declaration from becoming an unexplained incantation.

For a small immutable setting whose numeric meaning is obvious, constexpr int retryLimit{3} makes both type and compile-time availability clear. For a result produced while a program runs, const int measuredCount{expression} can describe a value you do not intend to change afterward. auto is useful when an initializer communicates a type clearly or when repeating a complex type would obscure the calculation. Neither spelling replaces the need to understand that initializer.

These examples concern local value objects, not every advanced const rule. Pointers, references, mutable members and overload resolution add details in later lessons. Do not assume a const declaration makes all reachable objects immutable or secures a program against malicious input. It communicates and enforces particular language constraints. Use the smallest claim supported by the actual object and access path rather than treating a keyword as a general security feature.

Three independent questions: const asks whether this object may be modified, constexpr requires a suitable constant initializer, and auto deduces the type from the initializer. const auto and constexpr auto combine the appropriate promises.
Stable value, constant expression and deduced type are distinct decisions—not a ladder of better keywords.

A const value can be determined at runtime

const int total{12} prevents ordinary assignment to total. If a value is initialized from a runtime measurement, it can still be const: after initialization, this part of the program does not intend to replace it. That stability helps a reader distinguish configuration from changing state. It also catches accidental assignments during maintenance. Do not remove const merely because a diagnostic complains; first check whether the assignment contradicts the requirement.

Some const integral objects initialized by a constant expression can themselves participate in constant expressions. Therefore 'const always means runtime' is incorrect. The useful beginner distinction is that const alone does not require every initializer to be a constant expression. A runtime-dependent initializer is permitted for an ordinary const variable, while the constexpr declaration used here rejects one. Explain the guarantee of the declaration rather than guessing when an optimizer will physically perform a calculation.

A copied const object does not automatically make the copy const. With const int setting{4}; auto copy = setting;, ordinary value deduction gives copy type int. You may then change copy without modifying setting. Use const auto copy = setting; if the copy should also remain stable. This lesson deliberately avoids reference deduction: later borrowing lessons explain when a declaration binds to an existing object instead of making a new value.

constexpr is a checked requirement, not a speed switch

For these scalar variable examples, constexpr requires a suitable constant-expression initializer and makes the object const. constexpr int days{7} is a named compile-time constant. constexpr int hours{days * 24} composes another one. If the initializer depends on input obtained while the program runs, that requirement cannot be satisfied. Calling a value a setting does not change the expression's properties; the compiler checks the initializer rather than your chosen identifier.

static_assert can check a constant-expression condition during translation. It is not an if statement and does not print an error at runtime. A failing static assertion prevents the build. Use it for a genuinely static invariant, such as a fixed number of hours matching your stated configuration. It cannot validate a ticket count entered tomorrow. Runtime inputs need runtime checks, which the conditions and input lessons develop separately.

Do not claim constexpr always makes an application faster. The optimizer can evaluate other expressions early when allowed, and the language guarantee is more specific than a general performance promise. Functions declared constexpr have additional rules and can sometimes run at runtime; function definitions are covered later. Here focus on variable declarations, readable constants and build-time requirements. Avoid expanding a small configuration example into an unsupported claim about every expression or library facility.

C++ PRACTICE RUNNER

Stable configuration with a checked constant

static_assert is checked at build time. The output statements still belong to program execution.

#include <iostream>
int main() {
    constexpr int days{7};
    constexpr auto hours = days * 24;
    static_assert(hours == 168);
    const auto label = "Hours: ";
    std::cout << label << hours << '\n';
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Hours: 168

After editing inputs, calculate a new prediction; this reference describes only the starter.

C++ PRACTICE RUNNER

Repair a runtime-dependent constexpr initializer

Intentional compile failure. argc is the runtime argument count, even if a particular launch is predictable. Replace constexpr with const to keep the value stable without requiring a constant expression.

int main(int argc, char**) { constexpr int count{argc}; return count; }
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

auto follows the expression—not the variable's purpose

auto count = 5 deduces int; auto ratio = 5.0 deduces double. auto average = 7 / 2 also deduces int because the division is integer arithmetic. Naming it average does not request a fractional result. Repair the operands before deciding whether auto or an explicit destination type is clearer. An explicit double destination would also fail to recover a fraction already discarded, so this is not simply an argument against deduction.

The value's type is fixed after deduction. auto does not produce a dynamically typed variable that changes type whenever assigned a different value. A later assignment must obey the normal conversion rules of the deduced type. Prefer initializer forms you can explain: auto x = 3 and auto x{3} deduce int here, but auto x = {3} creates an initializer_list<int>. The similar punctuation carries a different meaning, so do not mechanically swap the forms.

A string literal does not become std::string merely because auto appears. auto label = "ready" gives a pointer to const character data in this ordinary declaration. Use std::string label{"ready"} or auto label = std::string{"ready"} when you need an owning string object. Pointer syntax is introduced later; for now retain an explicit std::string where text ownership is part of the lesson. Type deduction removes repetition, not the obligation to know what type was selected.

C++ PRACTICE RUNNER

Trace the deduced numeric types

The static assertions verify types; the two printed results reveal the calculation. Change the operands, not just the variable names.

#include <iostream>
#include <type_traits>
int main() {
    auto truncated = 7 / 2;
    const auto ratio = 7.0 / 2;
    static_assert(std::is_same_v<decltype(truncated), int>);
    static_assert(std::is_same_v<decltype(ratio), const double>);
    std::cout << "Integer: " << truncated << '\n';
    std::cout << "Ratio: " << ratio << '\n';
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Integer: 3
Ratio: 3.5

After editing inputs, calculate a new prediction; this reference describes only the starter.

Choose a declaration a future reader can explain

A useful declaration records a real design decision. Prefer const when mutation is unnecessary; use constexpr when the value intentionally meets a constant-expression requirement; choose auto when the initializer makes the selected type understandable. It is reasonable to write explicit numeric types at an external boundary or where units and conversions need emphasis. There is no requirement to shorten every declaration or to reject deduction everywhere.

Review a change by asking what happens if the initializer changes. Replacing an integer literal with a floating-point literal may change an auto variable's type and downstream operations. Replacing a constant initializer with input may invalidate constexpr. Introducing an update may contradict const. Keep these consequences visible in tests and explanations. A declaration that compiles today is not automatically a sensible interface for every future input source.

LOCAL TERMINAL COMMANDS

Build and run the local examples

Save one complete example or lab solution as main.cpp. Choose your installed toolchain; run only after a successful build. Input examples wait for text in your terminal. The browser teaching interpreter does not support these full-language features.

macOS / Clang:
clang++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Linux / GCC:
g++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Windows Developer Command Prompt / MSVC:
cl /std:c++20 /EHsc /W4 main.cpp /Fe:lesson.exe
lesson.exe
APPLY THE CONCEPT

Independent lab

Represent a fixed page size and a stable runtime result. Calculate how many complete pages and leftover records exist for thirteen records.

Acceptance checklist

  • Use constexpr int pageSize{5}.
  • Use const int recordCount{13} as the fixed stand-in for a runtime observation.
  • Use const auto for quotient and remainder; explain their deduced integer types.
  • Check pageSize at compile time using static_assert.
  • Print Full pages: 2 and Leftover: 3 on separate lines.
Need a hint?
  1. Integer division answers how many complete groups fit.
  2. Remainder reports records outside those complete groups.
  3. A fixed stand-in can be const even though later real input would be runtime-dependent.
C++ PRACTICE RUNNER

Write and run your lab

Build your own solution before revealing the reference. Use the input field for normal, boundary and invalid cases; this runner executes your program but does not grade it.

#include <iostream>
#include <string>
#include <sstream>
#include <limits>

int main() {
    // Implement the acceptance checklist above.
    return 0;
}

TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Your lab evidence and notes

Record predictions, actual output, commands and the cause of each repair. Code is plain text: this scratchpad does not execute or grade C++.

Loading local notes…

Review your acceptance checklist and quiz explanations before marking this lesson complete. This is self-review, not a grade or certification.

Reveal the explained lab solution
C++ PRACTICE RUNNER

Reference lab solution

Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.

#include <iostream>
int main() {
    constexpr int pageSize{5};
    static_assert(pageSize > 0);
    const int recordCount{13};
    const auto fullPages = recordCount / pageSize;
    const auto leftover = recordCount % pageSize;
    std::cout << "Full pages: " << fullPages << '\n';
    std::cout << "Leftover: " << leftover << '\n';
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Expected output

Full pages: 2
Leftover: 3

pageSize is a named compile-time setting; static_assert rejects a nonpositive compile-time size.

recordCount is stable in this calculation. The example does not read a database or claim runtime input has already been implemented.

Both operands are int, so const auto produces const int results. With zero records the two output values become zero.

Review before moving on

Explain the lesson's objectives in your own words, compare your lab evidence with the acceptance checklist, and keep the first failing case alongside your repair. Completing a page is not proof that every C++ feature or input is supported.

  • Use const to express a stable value.
  • Distinguish runtime const values from constant expressions.
  • Use constexpr for suitable compile-time constants.
  • Predict auto from its initializer rather than its name.
  • Combine const or constexpr with deduction intentionally.

References and further reading

Original examples and exercises accompany these primary references. Tool-specific diagnostics and support depend on your installed version.

  • C++ draft: constexpr specifier
  • C++ draft: auto deduction
  • C++ Core Guidelines: const and constexpr
KNOWLEDGE CHECK

Check your understanding

Answer all five questions, then review the explanations. Answers are not saved or synced; completion is a separate self-review choice.

01Which keyword deduces a type from this variable's initializer?
02Can an ordinary const int be initialized from runtime input?
03What does auto average = 7 / 2 deduce?
04What happens when a static_assert condition is false?
05What does const auto add compared with auto for a simple value copy?
PREVIOUS LESSONC++ Data Types: int, double and Type Conversion
NEXT LESSONC++ Operators: Arithmetic, Comparison and Logic
ON THIS PAGELearning objectivesThree keywords answer three different questionsA const value can be determined at runtimeconstexpr is a checked requirement, not a speed switchauto follows the expression—not the variable's purposeChoose a declaration a future reader can explainIndependent labReviewKnowledge checkReferences
Course overview