6.x — Chapter 6 summary and quiz

Quick review

We covered a lot of material in this chapter. Good job, you’re doing great!

A compound statement or block is a group of zero or more statements that is treated by the compiler as if it were a single statement. Blocks begin with a { symbol and end with a } symbol, with the statements to be executed placed in between. Blocks can be used anywhere a single statement is allowed. No semicolon is needed at the end of a block. Blocks are often used in conjunction with if statements to execute multiple statements.

User-defined namespaces are namespaces that are defined by you for your own declarations. Namespaces provided by C++ (such as the global namespace) or by libraries (such as namespace std) are not considered user-defined namespaces.

You can access a declaration in a namespace via the scope resolution operator (::). The scope resolution operator tells the compiler that the identifier specified by the right-hand operand should be looked for in the scope of the left-hand operand. If no left-hand operand is provided, the global namespace is assumed.

Local variables are variables defined within a function (including function parameters). Local variables have block scope, meaning they are in-scope from their point of definition to the end of the block they are defined within. Local variables have automatic storage duration, meaning they are created at the point of definition and destroyed at the end of the block they are defined in.

A name declared in a nested block can shadow or name hide an identically named variable in an outer block. This should be avoided.

Global variables are variables defined outside of a function. Global variables have file scope, which means they are visible from the point of declaration until the end of the file in which they are declared. Global variables have static duration, which means they are created when the program starts, and destroyed when it ends. Avoid dynamic initialization of static variables whenever possible.

An identifier’s linkage determines whether other declarations of that name refer to the same object or not. Local variables have no linkage. Identifiers with internal linkage can be seen and used within a single file, but it is not accessible from other files. Identifiers with external linkage can be seen and used both from the file in which it is defined, and from other code files (via a forward declaration).

Avoid non-const global variables whenever possible. Const globals are generally seen as acceptable. Use inline variables for global constants if your compiler is C++17 capable.

Local variables can be given static duration via the static keyword.

Using statements (including using declarations and using directives) can be used to avoid having to qualify identifiers with an explicit namespace. These should generally be avoided.

Typedefs and Type aliases allow the programmer to create an alias for a data type. These aliases work identically to the aliased type. They can add legibility and reduce maintenance to code.

The auto keyword has a number of uses. First, auto can be used to do type inference / type deduction, which will infer a variable’s type from its initializer. Auto can also be used as a function return type to have the compiler infer the function’s return type from the function’s return statements, though this should be avoided for normal functions. Auto is used as part of the trailing return syntax. And finally, as of C++20, auto provides a shortcut for creating function templates. For now, you’ll generally use it for type deduction purposes.

Implicit type conversion is performed whenever one data type is expected, but a different data type is supplied. If the compiler can figure out how to do the conversion between the two types, it will. If it doesn’t know how, then it will fail with a compile error. Numeric promotion (or widening) occurs when a value from one fundamental data type is converted to a value of a larger fundamental data type from the same family (e.g. a short to an int). Numeric conversion occurs when we convert a value from a larger to a smaller type (e.g. int to short), or between different type families (int to double). Conversions that could cause loss of data are called narrowing conversions.

Explicit type conversion is performed when the programmer explicitly requests conversion via a cast. A cast represents a request by the programmer to do an explicit type conversion. C++ supports 5 types of casts: C-style casts, static casts, const casts, dynamic casts, and reinterpret casts. Generally you should avoid C-style casts, const casts, and reinterpret casts. static_cast is used to convert a value from one type to a value of another type, and is by far the most used-cast in C++.

Finally, C++ supports unnamed namespaces, which implicitly treat all contents of the namespace as if it had internal linkage. C++ also supports inline namespaces, which provide some primitive versioning capabilities for namespaces.

Quiz time

Question #1

Fix the following program:

Show Solution

Question #2

Write a file named constants.h that makes the following program run. If your compiler is C++17 capable, use inline constexpr variables. Otherwise, use normal constexpr variables. You can pick any value you like for max_class_size.


Show Solution

Question #3

Complete the following program by writing the passOrFail() function, which should return true for the first 3 calls, and false thereafter.

Show Hint

The program should produce the following output:

User #1: Pass
User #2: Pass
User #3: Pass
User #4: Fail
User #5: Fail

Show Solution

7.1 -- Control flow introduction
6.17 -- Unnamed and inline namespaces

314 comments to 6.x — Chapter 6 summary and quiz

  • EternalSkid

    Hello alex and nascardriver, first of all, thank you so much for making this awsome website and tutorials, it has greatly motivated me in my journey to programming! My solution is below, is there any way i can improve on this?

    #include <iostream>

    bool passOrFail() {
        static int pass{};
            return (pass <= 3);

    int main()
        std::cout << "User #1: " << (passOrFail() ? "Pass" : "Fail") << '\n';
        std::cout << "User #2: " << (passOrFail() ? "Pass" : "Fail") << '\n';
        std::cout << "User #3: " << (passOrFail() ? "Pass" : "Fail") << '\n';
        std::cout << "User #4: " << (passOrFail() ? "Pass" : "Fail") << '\n';
        std::cout << "User #5: " << (passOrFail() ? "Pass" : "Fail") << '\n';

        return 0;

    Thanks so much for your time! Have a great day.

  • nav

  • brainy

    How's this?

  • kio

    • TimT

      You can get rid of the conditional operator in the last line by using <= instead of >. The comparison operators give true/false values directly.

      return (counter <= 3);

  • Q.3 I'm using bit manipulation,probably.

    • mao zedung

      That's an overly complicated way of implementing that function (and does it even work for repetitions more than 8?), just create a static int, increment it and return a true or false depending on whether it's larger than 3

    • SuperNoob

      Missile for mosquito. You can add two numbers using:

      1. Your fingers
      2. The log table

  • Fox Paragorn

    I think that even if in this situation it works, for what the static is used should be taken more care. and lets suppose this code runs for a really LONG time (and its in a server that does not get reseted).
    then all this will fail the function should be clearly be sure not to fail for overflow and as such:

    bool passOrFail()
        static int passNumber{ 0 };
        constexpr int passLimit{ 3 };
        if (passNumber >= passLimit)
            return false;
        return true;
    what changes? well if it does not pass no longer adds anything so no matter how many times its called will never fail. (or may be i am overthinking it)

  • yeokaiwei

    Could you explain "return (--passes >= 0);"? for Quiz 3?

    It seems to be better placed in 5.4 - Increment/Decrement

    • Bryan

      "return (--passes >= 0);

      Let's say passes is 3.

      First, "--passes" decrements passes and returns it
      "return (2 >= 0);"

      Then, we take this returned value and we see if it is ">= 0" (greater than or equal to 0), returning true if yes, false if not.
      "return true;"

      Now we just return true.

  • yeokaiwei

    How about a PDF version of Without the header, footer, HTML links and comments.E.g. Learncpp 2020, Learncpp 2021, etc

    Just to print a hardcopy to refer to while reading the website and to scribble notes down.

  • Spero

    Hello, although I understand this.

    Where was this covered?

    I don't remember learning that return could be used like that.

    • Suyash Mallik

      you see, the type of function passOrFail is bool, which means that the value it will return will be true or false. Hence, return (--passes >= 0) means, "return true if --passes(passes - 1) is greater than or equal to zero". It's a really smart solution, here a static variable is used like a lap counter, with a decrease of 1 to the static variable 'passes' every time the function is called.

      P.S: here passes does not have anything to do with passing or failing, it is used in the sense of lap/turn. As in, I made 2 passes around the swimming pool.

    • Spero

      Yeah I get it. As I said I understand it I'm just wondering in what chapter or section it was taught as I don't remember ever learning it.

      • nascardriver

        This was never taught directly, but is a combination of different things you learned (`return`-statements, booleans, comparisons, pre-increment operator).

  • Tony

    I never seem to get the best solution in my answers... haha.

    This is my answer to the last question:
    bool passOrFail()
        static int x{ -1 };  // Change int to static, so it's not deleted after the block. So, when the func is called again, we still have the
                             // previous number which gets incremented again until x is 2.

        return { x < 3 };   // return true if x is 0, 1 or 2 (=    first 3 function calls)

    Is it ok as well?

    • Tony

      Nevermind, this is far better:

  • HektoR

    Not sure if this was intentional or not but code for Questions 1 works fine without fixing anything :)

    Apologies if this was intended though

  • Good job, you’re doing great!

  • John

    Hello! I have a question, why num = -num; changes it to a positive one? I don't understand

  • sami

    "Avoid dynamic initialization of static variables whenever possible."

    Shouldn't it be 'global' variables instead of 'static' variables?

  • Nahin

    I didn't get this sentence. Would you please clarify more?

    > Avoid dynamic initialization of static variables whenever possible.

  • AE35_Unit

    No 3.  I hope adding parameters was okay.

    • nascardriver

      The point was to use `static`. Make sure you understand the solution, then feel free to move on.

      You never need this. `<=` returns a `bool`, you can remove the conditional operator.

    • Fox Paragorn

      I think in this case is not ok to add an operator as its obvious this was made to use static, also it says this should not reset. so the static is correct.

  • AE35_Unit

    Question 2: Solution

    Seemed petty straight forward. Here is my go at No. 2
    Thanks for the great lessons.



  • Raffaello

    I was told inline namespaces are considered optional, but the solution for question #2 features one! I feel betrayed! :D

  • Gabe

    My Question #3:

  • Thomas Kennings

    "First, auto can be used to do type inference / type deduction), which will infer a variable’s type from its initializer."

    The parenthesis after "deduction" has no other paired parenthesis. 'Tis a typo I believe!

    Thomas Kennings

  • Lasha

    Provided answer to question #3 is incorrect. Line 6 says: return (passes-- >= 0);

    Should be: return (--passes >= 0)

  • Dudz

    Answer for question# 3

  • Ayrton Fithiadi Sedjati

    In the solution for question 2, I think as per best practice, #include "constants.h" should be put on top of #include <iostream>. I am referring to lesson 2.1 about header files.

    • nascardriver

      Good point! Lesson updated.

      • LorenzoC++

        for some reason, if i place #include "constants.h" on top of <iostream> it won't compile..anyone knows why? it's like #include <iostream> "shadowing" the file?!
        now it's working even if i put it back underneath iostream.. don't know what happened before..

  • Jimmy Hunter

    Typo = "The scope resolution operator tells the compiler that identifier specified by"
    Correction = "The scope resolution operator tells the compiler that the identifier specified by"

  • Jimmy Hunter

    May I suggest a sentence update?
    "Blocks begin with a { symbol, end with a } symbol, with the statements to be executed are placed in between."
    "Blocks begin with a { symbol and end with a } symbol with the statements to be executed placed in between."

    I'm not an english major but I think this is better.  I think the last "with" and the "are" conflict with each other in this usage.

  • Louis Cloete

    Two typos:

    Using statements (including using declarations and using directives) can be used to avoid having to quali*f*y identifiers with an explicit namespace.

    A cast represents *a* (not *an*) request by the programmer to do an explicit type conversion.

  • vie_man

    I've tried to follow best practices. Do you think this one is good enough?

  • Sejmou

    I implemented passOrFail() this way:

    In a previous chapter we learned that we should avoid post fix increment/decrement, also for performance reasons.
    is that solution much worse (performance-wise) than the suggested one?

  • An alternative version of passOrFail function:


    • jobel

      i did that a similar way, though slightly less efficient.

      (the naming for counter is left over from when i tried a for loop implementation)

Leave a Comment

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