Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Are static variables used in a lambda retained across calls of the function wherein the lambda is used? Or is the function object "created" again each function call?

Useless Example:

#include <iostream>
#include <vector>
#include <algorithm>

using std::cout;

void some_function()
{
    std::vector<int> v = {0,1,2,3,4,5};
    std::for_each( v.begin(), v.end(),
         [](const int &i)
         {
             static int calls_to_cout = 0;
             cout << "cout has been called " << calls_to_cout << " times.
"
                  << "Current int: " << i << "
";
             ++calls_to_cout;
         } );
}

int main()
{
    some_function();
    some_function();
}

What is the correct output for this program? Is it dependent on the fact if the lambda captures local variables or not? (it will certainly change the underlying implementation of the function object, so it might have an influence) Is it an allowed behavioural inconsistency?

I'm not looking for: "My compiler outputs ...", this is too new a feature to trust current implementations IMHO. I know asking for Standard quotes seems to be popular since the world discovered such a thing exists, but still, I would like a decent source.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
123 views
Welcome To Ask or Share your Answers For Others

1 Answer

tl;dr version at the bottom.


§5.1.2 [expr.prim.lambda]

p1 lambda-expression:
lambda-introducer lambda-declaratoropt compound-statement

p3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. (My note: Functions have a block scope.)

p5 The closure type for a lambda-expression has a public inline function call operator [...]

p7 The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator [...]

Since the compound-statement is directly taken as the function call operator's body, and the closure type is defined in the smallest (innermost) scope, it's the same as writing the following:

void some_function()
{
    struct /*unnamed unique*/{
      inline void operator()(int const& i) const{
        static int calls_to_cout = 0;
        cout << "cout has been called " << calls_to_cout << " times.
"
             << "Current int: " << i << "
";
        ++calls_to_cout;

      }
    } lambda;
    std::vector<int> v = {0,1,2,3,4,5};
    std::for_each( v.begin(), v.end(), lambda);
}

Which is legal C++, functions are allowed to have static local variables.

§3.7.1 [basic.stc.static]

p1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program.

p3 The keyword static can be used to declare a local variable with static storage duration. [...]

§6.7 [stmt.dcl] p4
(This deals with initialization of variables with static storage duration in a block scope.)

[...] Otherwise such a variable is initialized the first time control passes through its declaration; [...]


To reiterate:

  • The type of a lambda expression is created in the innermost scope.
  • It is not created anew for each function call (that wouldn't make sense, since the enclosing function body would be as my example above).
  • It obeys (nearly) all the rules of normal classes / structs (just some stuff about this is different), since it is a non-union class type.

Now that we have assured that for every function call, the closure type is the same, we can safely say that the static local variable is also the same; it's initialized the first time the function call operator is invoked and lives until the end of the program.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...