7.16 — Lambda captures

Capture clauses and capture by value

In the previous lesson, we introduced this example:

Now, let’s modify the nut example and let the user pick a substring to search for. This isn’t as intuitive as you might expect.

This code won’t compile. Unlike nested blocks, where any identifier defined in an outer block is accessible in the scope of the nested block, lambdas can only access specific kinds of identifiers: global identifiers, entities that are known at compile time, and entities with static storage duration. search fulfills none of these requirements, so the lambda can’t see it. That’s what the capture clause is there for.

The capture clause

The capture clause is used to (indirectly) give a lambda access to variables available in the surrounding scope that it normally would not have access to. All we need to do is list the entities we want to access from within the lambda as part of the capture clause. In this case, we want to give our lambda access to the value of variable search, so we add it to the capture clause:

The user can now search for an element of our array.


search for: nana
Found banana

So how do captures actually work?

While it might look like our lambda in the example above is directly accessing the value of main‘s search variable, this is not the case. Lambdas might look like nested blocks, but they work slightly differently (and the distinction is important).

When a lambda definition is executed, for each variable that the lambda captures, a clone of that variable is made (with an identical name) inside the lambda. These cloned variables are initialized from the outer scope variables of the same name at this point.

Thus, in the above example, when the lambda object is created, the lambda gets its own cloned variable named search. This cloned search has the same value as main‘s search, so it behaves like we’re accessing main‘s search, but we’re not.

While these cloned variable have the same name, they don’t necessarily have the same type as the original variable. We’ll explore this in the upcoming sections of this lesson.

Key insight

The captured variables of a lambda are clones of the outer scope variables, not the actual variables.

For advanced readers

Although lambdas look like functions, they’re actually objects that can be called like functions (these are called functors -- we’ll discuss how to create your own functors from scratch in a future lesson).

When the compiler encounters a lambda definition, it creates a custom object definition for the lambda. Each captured variable becomes a data member of the object.

At runtime, when the lambda definition is encountered, the lambda object is instantiated, and the members of the lambda are initialized at that point.

Captures default to const value

By default, variables are captured by const value. This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them. In the following example, we capture the variable ammo and try to decrement it.

In the above example, when we capture ammo, a new const variable with the same name and value is created in the lambda. We can’t modify it, because it is const, which causes a compile error.

Mutable capture by value

To allow modifications of variables that were captured by value, we can mark the lambda as mutable. The mutable keyword in this context removes the const qualification from all variables captured by value.


Pew! 9 shot(s) left.
Pew! 8 shot(s) left.
10 shot(s) left

While this now compiles, there’s still a logic error. What happened? When the lambda was called, the lambda captured a copy of ammo. When the lambda decremented ammo from 10 to 9 to 8, it decremented its own copy, not the original value.

Note that the value of ammo is preserved across calls to the lambda!

Capture by reference

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument.

To capture a variable by reference, we prepend an ampersand (&) to the variable name in the capture. Unlike variables that are captured by value, variables that are captured by reference are non-const, unless the variable they’re capturing is const. Capture by reference should be preferred over capture by value whenever you would normally prefer passing an argument to a function by reference (e.g. for non-fundamental types).

Here’s the above code with ammo captured by reference:

This produces the expected answer:

Pew! 9 shot(s) left.
9 shot(s) left

Now, let’s use a reference capture to count how many comparisons std::sort makes when it sorts an array.

Possible output

Comparisons: 2
Honda Civic
Toyota Corolla
Volkswagen Golf

Capturing multiple variables

Multiple variables can be captured by separating them with a comma. This can include a mix of variables captured by value or by reference:

Default captures

Having to explicitly list the variables you want to capture can be burdensome. If you modify your lambda, you may forget to add or remove captured variables. Fortunately, we can enlist the compiler’s help to auto-generate a list of variables we need to capture.

A default capture (also called a capture-default) captures all variables that are mentioned in the lambda. Variables not mentioned in the lambda are not captured if a default capture is used.

To capture all used variables by value, use a capture value of =.
To capture all used variables by reference, use a capture value of &.

Here’s an example of using a default capture by value:

Default captures can be mixed with normal captures. We can capture some variables by value and others by reference, but each variable can only be captured once.

Defining new variables in the lambda-capture

Sometimes we want to capture a variable with a slight modification or declare a new variable that is only visible in the scope of the lambda. We can do so by defining a variable in the lambda-capture without specifying its type.

userArea will only be calculated once when the lambda is defined. The calculated area is stored in the lambda object and is the same for every call. If a lambda is mutable and modifies a variable that was defined in the capture, the original value will be overridden.

Best practice

Only initialize variables in the capture if their value is short and their type is obvious. Otherwise it’s best to define the variable outside of the lambda and capture it.

Dangling captured variables

Variables are captured at the point where the lambda is defined. If a variable captured by reference dies before the lambda, the lambda will be left holding a dangling reference.

For example:

The call to makeWalrus creates a temporary std::string from the string literal “Roofus”. The lambda in makeWalrus captures the temporary string by reference. The temporary string dies when makeWalrus returns, but the lambda still references it. Then when we call sayName, the dangling reference is accessed, causing undefined behavior.

Note that this also happens if name is passed to makeWalrus by value. The variable name still dies at the end of makeWalrus, and the lambda is left holding a dangling reference.


Be extra careful when you capture variables by reference, especially with a default reference capture. The captured variables must outlive the lambda.

If we want the captured name to be valid when the lambda is used, we need to capture it by value instead (either explicitly or using a default-capture by value).

Unintended copies of mutable lambdas

Because lambdas are objects, they can be copied. In some cases, this can cause problems. Consider the following code:



Rather than printing 1, 2, 3, the code prints 2 twice. When we created otherCount as a copy of count, we created a copy of count in its current state. count‘s i was 1, so otherCount‘s i is 1 as well. Since otherCount is a copy of count, they each have their own i.

Now let’s take a look at a slightly less obvious example:



This exhibits the same problem as the prior example in a more obscure form. When std::function is created with a lambda, the std::function internally makes a copy of the lambda object. Thus, our call to fn() is actually being executed on the copy of our lambda, not the actual lambda.

If we need to pass a mutable lambda, and want to avoid the possibility of inadvertent copies being made, there are two options. One option is to use a non-capturing lambda instead -- in the above case, we could remove the capture and track our state using a static local variable instead. But static local variables can be difficult to keep track of and make our code less readable. A better option is to prevent copies of our lambda from being made in the first place. But since we can’t affect how std::function (or other standard library functions or objects) are implemented, how can we do this?

Fortunately, C++ provides a convenient type (as part of the <functional> header) called std::ref that allows us to pass a normal type as if it were a reference. By wrapping our lambda in a std::ref, whenever anybody tries to make a copy of our lambda, they’ll make a copy of the reference instead, which will copy the reference rather than the actual object.

Here’s our updated code using std::ref:

Our output is now as expected:


Note that the output doesn’t change even if invoke takes fn by value. std::function doesn’t create a copy of the lambda if we create it with std::ref.


Standard library functions may copy function objects (reminder: lambdas are function objects). If you want to provide lambdas with mutable captured variables, pass them by reference using std::ref.

Best practice

Try to avoid lambdas with states altogether. Stateless lambdas are easier to understand and don’t suffer from the above issues, as well as more dangerous issues that arise when you add parallel execution.

Quiz time

Question #1

Which of the following variables can be used by the lambda in main without explicitly capturing them?

Show Solution

Question #2

What does the following code print? Don’t run the code, work it out in your head.

Show Solution

Question #3

We’re going to write a little game with square numbers (numbers which can be created by multiplying an integer with itself (1, 4, 9, 16, 25, …)).

Ask the user to input 2 numbers, the first is the square root of the number to start at, the second is the amount of numbers to generate. Generate a random integer from 2 to 4, and square numbers in the range that was chosen by the user. Multiply each square number by the random number. You can assume that the user enters valid numbers.

The user has to calculate which numbers have been generated. The program checks if the user guessed correctly and removes the guessed number from the list. If the user guessed wrong, the game is over and the program prints the number that was closest to the user’s final guess, but only if the final guess was not off by more than 4.

Here are a couple of sample sessions to give you a better understanding of how the game works:

Start where? 4
How many? 8
I generated 8 square numbers. Do you know what each number is after multiplying it by 2?
> 32
Nice! 7 number(s) left.
> 72
Nice! 6 number(s) left.
> 50
Nice! 5 number(s) left.
> 126
126 is wrong! Try 128 next time.
  • The user chose to start at 4 and wants to play with 8 numbers.
  • Each square number will be multiplied by 2. 2 was randomly chosen by the program.
  • The program generates 8 square number, starting with 4 as a base:
  • 16 25 36 49 64 81 100 121
  • But each number is multiplied by 2, so we get:
  • 32 50 72 98 128 162 200 242
  • Now the user starts to guess. The order in which in guesses are entered doesn’t matter.
  • 32 is in the list.
  • 72 is in the list.
  • 126 is not in the list, the user loses. There is a number in the list (128) that is not more then 4 away from the user’s guess, so that number is printed.
Start where? 1
How many? 3
I generated 3 square numbers. Do you know what each number is after multiplying it by 4?
> 4
Nice! 2 numbers left.
> 16
Nice! 1 numbers left.
> 36
Nice! You found all numbers, good job!
  • The user chose to start at 1 and wants to play with 3 numbers.
  • Each square number will be multiplied by 4.
  • The program generates these square numbers:
  • 1 4 9
  • Multiplied by 4
  • 4 16 36
  • The user guesses all numbers correctly and wins the game.
Start where? 2
How many? 2
I generated 2 square numbers. Do you know what each number is after multiplying it by 4?
> 21
21 is wrong!
  • The user chose to start at 2 and wants to play with 2 numbers.
  • Each square number will be multiplied by 4.
  • The program generates these numbers:
  • 16 36
  • The user guesses 21 and loses. 21 is not close enough to any of the remaining numbers, so no number is printed.

Use std::find (6.18 -- Introduction to standard library algorithms) to search for a number in the list.
Use std::vector::erase to remove an element, e.g.

Use std::min_element and a lambda to find the number closest to the user’s guess. std::min_element works analogous to std::max_element from the previous quiz.

Show Hint

Show Solution

7.x -- Chapter 7 comprehensive quiz
7.15 -- Introduction to lambdas (anonymous functions)

129 comments to 7.16 — Lambda captures

  • Apple

    Hey! Thanks for the tutorials. It's really amazing how a good source like this is free. Could you please rate my code? Thanks in advance!

    • nascardriver

      - Only use regular for-loops if you need a counter variable. Both of your for-loops can be replaced with range-based for-loops.
      - `changeAllValues` is `std::transform`.
      - Use `numbers.empty()` to check if the vector is empty.
      - Using `std::function` in line 63 is unnecessary, you can use `auto`.

      Good job :)

  • Typo(maybe!)

    >>Lambdas might look and function like nested blocks, but they work slightly differently (and the distinction is important).

    I have read the sentence above several times:

    Shouldn't it be "Lambdas might look like function and nested blocks, but"

    >>When the compiler encounters a lambda definition, it creates a custom object definition for the lambda. Each captured variable becomes a data member of the object. At runtime, when the lambda definition is encountered, the lambda object is instantiated, and the members of the lambda are initialized at that point.

    Do those sentence mean when a compiler encounters a lambda, it creates a class (blueprint) of that lambda definition with its captures as data member and then in the run-time it is like instantiating an object of that lambda class ?

    • nascardriver

      It was correct as written, "function" meant "work" in this context. Though that didn't make sense, so I removed that part from the sentence.

      > Do those sentence mean [...]
      That's right. Though, lambdas can be evaluated at compile-time (if their definition allows it).

  • Jacob

    Small edit to problem 3: 2 is not a square number by the definition you provide.

  • Nexteon

    I was confused about what "states" were when you mentioned them near the end. Did I miss information on that in the previous lesson or is their more of a description on it?

  • Amir

    notes please

  • Amir

    I want post my code but afraid that its not showing as formatted even for me looks like formatted and I did auto format (ctr+k/ctr+d) and manually also with clicking xD it's really formatted but i don't know how it's not showing here . if you have any ideas about fix formatting settings please share (I did  step lesson 2.6 ) , since automatic format not working as it should.

  • CvB

    I've enjoyed this quiz and chapter very much, thanks Alex and nascardriver! Below is my code; I've chosen to keep the game itself in one function, as I find that a bit easier to read. I also have one question:

    I was struggling with the 4 number deviation. When I read the hint, mentioning std::abs, it gave me the push I needed to get it. I did look for myself on cppreference and google if there were functions which could help me, but I didn't stumble onto this one. Do you have tips to go about finding the function I need in future programs?

    • nascardriver

      - Avoid abbreviations. Name variables descriptively.
      - Use an auto-formatter.
      - Make compile-time constants `constexpr`.
      - Line 35 causes undefined behavior if `gameNum` is empty.
      - Line 54: There's `gameNum.empty()`.
      - `enterNums` doesn't care if `randInt` is random. For `enterNums`, `randInt` is a multiplier. `randInt` is confusingly named.
      - You're not using `enterNums::element`. You could have looped over `startNum` directly, or removed `startNum` and use `element`.

      If you write a really trivial function (That implies you should write functions) that you could use in every project, there's probably is standard function for it. Google for whatever your function does and you should find the corresponding standard function.
      If you don't know what you're looking for, but you have a general idea of what you need, look at the libraries in the overview ( ). In this quiz, you could have used Containers, Algorithms, Numerics. Then open up the library references and browse their contents.
      Finding the right functions can be difficult. You'll inevitably write code for which you could have use standard functions. Some time later you'll see someone else use the standard function that you needed and you think about all the time that this could have saved you.

      Sometimes cppcheck ( ) can detect that you're doing something that you shouldn't, for example

      • CvB

        Thanks for your excellent advice nascardriver! I just realized this whole chapter is about functions and passing values and I'm like: 'I think it's more readable like this'...sorry about that. I'll have some fun with this code using your recommendations and the intention of this chapter. :)

  • hari

    The tutorials are really good!!. I have a doubt regarding the scope of the variable copy created when lambda is called. Each time when the lambda is fully executed, the local copy variable should be destructed right. Then how is the value of ammo decrementing when we call the lambda three times in the example.

    • nascardriver

      Captured variables aren't local variables, they're stored in the lambda object. They only get destroyed when the lambda gets destroyed. The copy of `ammo` gets created when the lambda is defined, and destroyed when the lambda is destroyed. The calls in between don't matter.

  • Alex & Nascardriver, thank you for the excellent tutorials. After studying several sites & youtube tutorials I wish I stumbled here first; the quality is excellent.

    I'm unable to run the previous tutorial of string_view, with the error being "access is denied". Which I thought may be a corruption with the build configuration in Virtual Studio, from a previous lesson overloading the stack with too much memory, but that's not the case. I created several test projects since, & all are running fine, with the exception to this lesson.

    I'm also not able to run the lambadas either; both providing the error because a default capture mode has not been specified.

    In the meantime I'm moving forward with other tutorials, but I thought I'd check if it's an error with my system at the moment or if you guys have no issues with running these examples? No doubt I've made some silly error somewhere.

    I have the project set to C++17 language version & all of the header files are referenced correctly. Tried restarting VS also to flush out the memory.
    I've also tried copying & pasting the code from the website but it fails with these errors.

    My apologies for raising such a trivial issue.

    • nascardriver

      The examples are fine, there must be something wrong on your side. "access is denied" might be caused by you not stopping the program after you launched it the last time, so VS can't update the binary. I can't assist you any further.

      "capture mode has not been specified" sounds more like an issue with the compiler configuration. What's your compiler version and exact error message?

  • Strato

    I'm proud of myself because i solved it without looking at the hints or other students code, but my code is horrible to read, i would like to learn how to structure the code in the way that the solution is solved, where can i read about that or is just practice and experience?
    Thank you so much for all the work you do.

    • nascardriver


      Good job, you applied a lot of things in your code that you learned in the lessons up to now :)

      What makes your code difficult to read is the lack of functions and whitespace. For example, there should be no random number generation in `getVectorOfSquares` (Related: Your function doesn't generate random numbers, because you recreate `mersenne` with every call). Have a look at the solution to see what other functions you could have written.

      If you have to write long functions, which should be rare, add an empty line between lines that are mostly unrelated. For example reading user input, and searching for a number. Or defining variables and starting an `if` or `while`. Just like it's hard to read written text without paragraphs, it's hard to read code that's written without a pause.

  • giang

    Phew, seems quite easy at first glance, but took me a whole morning. So this is my solution for the last quiz. I didn't use min_element because I thought that if the guess is different from the number by 4, then it means that the number is also the closest. Also, I could use 'abs' even though I didn't include <math.h>. Is that possible?? Thanks so much if you can give me some comments or feed-backs about my codes or mistakes :)

    • nascardriver

      - Name variables descriptively, avoid abbreviations. Your functions are useless unless the caller reads their definition. Always imagine you're writing code for someone else to use.
      - Pass fundamental types by copy. Passing them by reference is slower.
      - `aRandomToMultiply` isn't random when called repeatedly. Do you remember why?
      - Use `false`/`true` for booleans, not 0/1. If 0/1 is still used in a lesson, please let me know.
      - Line 90-93: Duplicate comparisons. This can be solved by using a function.
      - Line 93: Unnecessary use of `std::endl`.
      - You've mixed copy initialization, direct initialization, and list initialization syntax. All you need is list initialization, apart from line 81, where direct initialization is correct, because list initialization would change the behavior.

      You're missing the include to cstdlib for `std::system` and cmath for `std::abs`. Your code compiles for you, because some other header is including these headers. That's non-standard. If you use something, include its header. `std::system` also makes your code non-portable. Only use it for testing.

      > if the guess is different from the number by 4, then it means that the number is also the closest
      Start with 1 2 4
      User removes 2
      User enters 3
      You say the closest was 1, when it was really 4

  • Constantine

    Here's my solution to the last problem on the quiz. I had to use my own vector element erasement function because std::vector::erase can't be found by my compiler. I am using Visual Studio 2019. And I also used my countryman's random library.

    • nascardriver

      `std::vector::erase` was added in C++11, you must be using it wrong or have a misconfigured project.

      - Line 8,9: Remove the if-statement, adjust the loop's condition.
      - `long long` is not the type used by `std::vector` for indexes. It's `std::vector::size_type`. I understand that's a lot of typing. Although wrong, you can use `std::size_t`. That's not the correct type either, but closer than `long long`.
      - If you don't modify a reference parameter, make it `const` to allow the caller to use `const` arguments and the give the caller certainty that you don't change their data.
      - Line 39,40: This is easier to understand if you iterate from 0 to `range` and do the addition in the loop'd body.
      - `i;` in line 56 doesn't do anything, you can use just a semicolon.
      - Line 66: This is confusing too. `if (close_int != -1)`.

      The purpose of this quiz was to make you practice the use of lambdas. Please read the solution and make sure you understand it and look for where you could have used lambdas in your code.

  • Vir1oN

    Did I understand it correctly, that std::function variable as a parameter essentially ignores, that it was initialized with reference of a lambda (since we're passing it by reference), by making a copy anyway?

    In other words, why is it necessary to explicitly pass in a reference by using std::ref, when in most cases passing by reference (i.e. adding '&' to a function parameter) is enough?

    • nascardriver

      When you pass a callable object (eg. a lambda) to `std::function`, it creates a copy of the object (Note that `count` is not a `std::function`, a temporary `std::function` gets created every time we call `invoke`).
      If you pass a `std::reference_wrapper` (Output of `std::ref`) to `std::function`, its special handling for `std::reference_wrapper` kicks in and only a reference is stored.

  • Tom

    Hi! I wanted to let you know that the hyperlink (to lesson 7.8-Function pointers) that is at the bottom of Question 3 doesn't work. It leads to an error message.

  • salah

    how can I reset 'result__' to 1 in every call?
    in this functor I want to calculate the factorial on a number, but the problem with 'result__' ..!

  • salah

    Which one is more preferred to use in the case where the body of the function is small
    inline function or lambda ?

    • nascardriver

      You never need `inline` to inline a function. `inline` is used to make objects unique even if they have multiple definitions.
      If you need your function in more than one place, use a regular function. If you only need that function once (And you can't imagine ever needing it again), use a lambda.

  • Check out this code please. It's a little different from what you did but I did it myself. Please if there's anything that can be corrected or be done better, I'll appreciate if you tell me. It works correctly though.

    • nascardriver

      - Initialize variables with brace initialization for higher type safety.
      - Seed random number generators only once. Verify that your seed generator is available. `random_gen` is an expensive call and might not be random. Check `std::random_device::entropy()` and fall back to another source if it's not available.
      - Your formatting is misleading and inconsistent. Use your editor's auto-formatting feature.
      - Use single quotation marks for characters. Strings are more expensive.

      Your code would benefit from using more functions. Cramming everything into one function is harder to maintain and read. There's room for improvement, but you made it, good job!

  • Andy

    Can anyone help out with why my code for Q3 is crashing? As far as I can tell it doesn't like my if statement but I can't see why there's a problem.

    • nascardriver

      Using an iterator from one list in the function of another list causes undefined behavior.

      The elements in the two lists can map to each other via their index, so you can calculate the iterator in `numbers` that corresponds to `exactMatch`.

      `std::distance` returns the distance between the two iterators. By passing in `diffs.begin()`, we get the index of `exactMatch` back out.

      • Andy

        Oh that does make sense, thanks a lot! I assumed that wouldn't work but I didn't notice since the `printf("exact match")` line just before that one was never printed. Turns out it crashed before flushing stdout. I'll have to be more careful of that in future.

  • Jas

    I am confused when you pass the Lambda object into the external function, with use of std::ref.

    Why do we have this problem? The input parameter is an std::function<void(void>>& reference, so surely it will be directly querying the memory location of the argument lambda? Why do we have to wrap the lambda in std::ref?

    • nascardriver

      A lambda is _not_ a `std::function`. When you pass a lambda to a function that wants a `std::function`, a new `std::function` is constructed and the lambda object is copied. A similar thing happens when you wrap the lambda in `std::ref`. A new `std::function` is constructed, but rather than copying the lambda object, a reference is stored.

  • AbraxasKnister

    I don't seem to understand the difference between (the first questions) 'e' and 'f': `const int e{}; const int f{getValue()};`. I was under the impression that 'e' being initialisable at compile time shouldn't make a difference (ie it should only make a difference in optimisation). I thought "not usable in the lambdas body in general, but might be for most implementations" (not a strict yes, rather "no, but actually yes"). If it does make a difference, what is the difference between `const int e{0};` and `constexpr int a{0};`?

    • nascardriver

      `f` calls a function, which is not a `constexpr` function. `getValue` might need a runtime, eg. if it's using `std::cin`. To the caller, it's not obvious whether or not `getValue` requires a runtime or not. Unless a function is marked `constexpr`, it's assumed to require a runtime.
      Even without marking a function as `constexpr`, compilers might still evaluate them at compile-time if that's possible. But this is just an optimization and doesn't affect language rules.
      Because `getValue` is no `constexpr`, `f` is not a compile-time constant and not usable in constant expressions and can't be implicitly captured.

      `e` is compile-time constant, because it's initialized with empty curly braces. Even though it's not marked as `constexpr`, it's usable in constant expressions. If we initialized it with a literal (eg. 4), nothing would change.

      > what is the difference between `const int e{0};` and `constexpr int a{0};`?
      I don't think there's a difference when you initialize the variables with literals.
      You will get different behavior if you initialize `e` and `a` with variables or return values.

      Here, `a`'s definition is illegal unless `i` and `fn` are constant expressions. `e` works in either case.

      • AbraxasKnister

        The difference between const and constexpr is that you can't use const variables where a compile time constant is required (otherwise it wouldn't make sense that consts can be initialized with runtime dependent values). It seems odd that you need to look at the variable definition, ie need more than type information, to know wether you can access the variable in the lambda or not. That's what lead me to think that allowing the use of a const whose value can be inferred at compile time was more like a convenience implemented by most compilers than a language feature (otherwise: where else do I need to know more than the type information of a variable top know what I can do with it?)

        • nascardriver

          > you can't use const variables where a compile time constant is required
          That's not correct, for example

          `constexpr` needs a compile-time constant, which `i` is.

          > where else do I need to know more than the type information of a variable top know what I can do with it?
          Templates, because you don't even know their type. Templates are covered later, they're kind of like `auto`.
          There are probably more situations, but I can't think of any right now.

          • AbraxasKnister

            Hmm... yes, it works. As in "I don't believe anyone can find a compiler that doesn't implement it as expected".

            Not really much other things I could think of. If I understand the const or constexpr qualifiers correctly (too intimidated by the standard to read it) NONE of these are required to compile, if they do, they do so because you're taking advantage of a compiler extension.

            EDIT: `auto f{ []() -> double { return d; };` had a missing '}'. "std::array::size_t" doesn't seem to exist, use "std::size_t" instead. Everything compiles fine when the two "const" are exchanged to "constexpr" (the assertion fails of course).

            • nascardriver

              No compiler extensions or implementation-defined behavior. All of this is standard behavior. The only issue in your code is `d`. Only `const` _integral_ types are usable in constant expressions ( See ). `double` is not an integral type.
              If you make `d` `constexpr`

              all of your code is valid and, apart from the static_assert, must compile. If a compiler does _not_ compile your code after this change, the compiler is _not_ compliant with the standard.

              If something can be evaluated at compile-time, mark it `constexpr` yourself. This will save you and readers of your code headache and assists the compiler.

              • AbraxasKnister

                Ah, there it is. Thanks a lot, now I see it. So we have a convenience driven inconsistency in the standard that causes const to mean constexpr in the case of a constexpr initialized integral type variable (and probably in other cases as well), which causes all of

                to work, but leads all of it to fail if 'i' is defined by

                At least it does for me on gcc with the "-pedantic-errors" flag. May I suggest to alter Question 1 to also refer to variables

                in the lambda definition and maybe put this detail into chapter 4.13?

                • nascardriver

                  This lesson isn't about which variable are and aren't usable in constant expressions. I think including this example would be more confusing than helpful. Instead, this should be part of a `constexpr` lesson. As of now, `constexpr` variables are introduced on learncpp, but not fully discussed. I'll keep this example in mind and add it once we have more lessons about `constexpr` (This won't happen anytime soon, because C++20 changes some `constexpr` rules which we'll have to get familiar with before writing a lesson).

                  • AbraxasKnister

                    Thanks for your effort in this conversation. I learned a lot. I didn't expect a lesson specifically on `constexpr` and therefore suggested lesson 4.13. I'm exited to see it once it's created, I'm a huge fan of this series. I just find the integral types are more confusing here, ie I didn't expect 'e' to be treated as a compile time constant, even though its value can be inferred.

  • Paprika

    I got output of 1 2 3 in the example code.

    The only different part is that I'm using type std::function<void(void)> to create the variable but not auto.Bcz I can't pass the compile by using auto type defination.
    IN VS2013 & c++11 env

    • nascardriver

      Seems like VS2013 isn't fully C++11 capable, consider upgrading your compiler.

      If you create a `std::function` rather than keeping the real lambda type, you only have 1 function and don't create any copies when you pass it to `invoke`. With the original lambda type, a `std::function` has to be created every time `invoke` is called (Unless we use `std::ref`, in which case `std::function` only stores a reference).

      • IceAlphaka

        Hi Nascardriver,

        Thanks for a good tutorial.
        And I'd like to add more to what "Paprika" said.
        I'm using mingw32 9.2.0 and tested the exactly same code under C++11, C++17 & C++20.
        C++11 shows 1, 1, 1 while C++17 & 20 are showing 1, 2, 3.



  • Mn3m

    I had a problem when I attempted to use lambda to find the closest number to the user's guess. Can you explain how you used min_element and lambda together?

    What does (int a, int b) mean here? Are they two adjacent values in the vector?

    • nascardriver

      `a` and `b` are arbitrary elements in the vector. `std::min_element` needs some way of comparing the elements, that's what the function is there for. Your `getClosestAnswer` doesn't return the closest answer, but the first element that is closer than or equal distance 4. If the vector contains 2 and 4 and you enter 5, it will return 2.
      Have a look at the second possible implementation on cppreference
      That should help you to understand what `min_element` does.

      • Mn3m

        Thx for pointing that out! I didn't pay attention to the fact that my getClosest.. won't return the closest but the first closest. I only tried the test cases/ examples provided. I modified my function
        to solve this problem

        • nascardriver

          That won't return the closest if the closest is further away than 9999999. You can get the maximum integer value with `std::numeric_limits::max()` (#include ).
          Initializing variables with brace initialization gives you higher type-safty.
          If you don't modify a reference parameter, make it `const`.
          Binding references to fundamental types is slower than copying them (`i`).

          • Mn3m

            I modified it yet again :d.

            • nascardriver

              The `const` went to the wrong parameter. `numberList` is a reference which you don't modify, it should be `const`. The way the caller is assured that their vector stays unmodified.
              You don't need `dummy`, you can initialize `min` right away. This has no extra cost.
              You're calculating `std::abs(guess - i)` twice for every element, that's wasteful. You don't need to check `<= 4` inside the loop at all (And shouldn't, because that's unexpected behavior based on the function name). Say there is no element close enough to `guess`. `getClosestAnswer` returns INT_MAX, which is then used by the caller. You need a way to signal to the caller that no element was found. Sorry for finding something new every time :D

              • Mn3m

                Hahahaha, I actually like this kinda feedback as it helps me to improve. Regarding the last point, I already gave the caller a conditional statement to check if the value returned is still max_int or it was updated :o.

  • kavin

    Hi,finally i figured out the my mistake in quiz 3 ! Pls ignore/delete my previous 2 posts in this chapter. Here is my corrected solution. Let me know if i could improve the code in any way.

  • kavin

    Line 75 condition is wrong in my answer. Sorry, just double checked it today. How should i write that condition?
    @nascardriver Looks like i misunderstood std::abs. Could u explain me how std::min_element and std::abs word together ?

  • kavin

    Hi, this is my answer to quiz 3. Let me know if i need to correct anything :)

    Also under "Capture by reference" 2nd example i get a different output when i run the code and #include<array> is missing.


    Comparisons: 4
    Honda Civic
    Toyota Corolla
    Volkswagen Golf

    And how does  return (a.make < b.make); compare strings? Does it compare by number of characters in the string or by alphabetical order of strings? Please let me know.

    • nascardriver


      - Line 28: Initialize variables with brace initialization.
      - `lcount` can be removed and the condition replaced with `!array.empty()`.
      - Line 80 always compares `true` because of line 68/72.

      > i get a different output when i run the code
      The sorting algorithm `std::sort` uses is implementation-defined. 4 comparisons is pretty bad for a list with 3 elements, but this isn't something you should worry about.

      > #include is missing
      Added, thanks!

      > how does return (a.make < b.make); compare strings?
      std::basic_string::compare compares character by character, until they differ.

  • Suyash

    Here's my solution to the problem... Will patiently wait for your review...

    Is this the proper usage of `static` keyword in the context of functions if I wish to prevent it from being used in other files of the project. Or, is it redundant due to them not being declared in the header? And, is there any benefit in terms of performance (either runtime or compile/build time) when using `static` (w/ functions)?

    Another question is why can't a person with a pre-C++17 compiler do this exercise?

    1. guess_squares.h

    2. guess_squares.cpp