1
class A {
    public:
        int a;
        char b;
        double c;
        A ( int x, char y, double z ) : a(x), b(y), c(z){}
};

int main(){

    auto lambda = []( auto x ) {
        static auto y = x;
        // y = x;
        return y;
    };

    int    a = lambda(1);
    char   b = lambda('a');
    double c = lambda(1.5);
    A      d = lambda( A( 2, 'b', 2.5 ) );

    return 0;
}

This code compiles in Clang 3.8.0 and GCC 5.4.0, and works fine. However, taking into account that variable y is static:

  • What is the type of variable y? Does the type of y change in every call to the lambda?
  • Is variable y initialized in every call in spite of being static? The commented assignment // y = x is not needed to update the value of variable y.
  • Is this behaviour C++14 Standard compliant?

If I print the sizeof(y) in each call I get 4, 1, 8 and 16 respectively.

On local and global static variables in C++

Community
  • 1
  • 1
J L
  • 563
  • 3
  • 17
  • 2
    A generic lambda's `operator (...)` is more or less a *function-template*. Hence the static variables will be created for every separate instantiation of the function. – WhiZTiM Feb 22 '17 at 16:11

2 Answers2

4

Your lambda is generic. Which means that this is a template in disguise. The situation is handled in accordance with general rules for template specializations.

For every specific deduced type of parameter x you get a separate specialization of your function. So, yes for each specific type of x you get a separate copy of y. But the underlying mechanism is not somehow localized at your static, it is the whole body of your function that gets "copied" to produce a separate independent implementation of the function.

Each specialization of your lambda will have a separate copy of y, which will be initialized only once at the first call to that specific specialization.

The situation is virtually equivalent to a more explicit

template <typename T> void foo(T x)
{
  static T y = x;
  std::cout << y << std::endl;
}

int main()
{
  foo(1);
  foo('a');
  foo(1.5);
  foo(3.0);
}

which outputs 1, a, 1.5 and 1.5.

In this example you get three independent specializations of foo: foo<int>, foo<char> and foo<double>. Each version of foo gets its own version of y, meaning that there are three different static ys in this example. The first call to each specialization will initialize y and the subsequent calls will not re-initialize it. In this case call to foo(1.5) initializes y for foo<double>, but the subsequent call to foo(3.0) does not.

The same thing happens in your case as well, it just uses a different syntax.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Great! That makes perfect sense. I was also printing the address of variable `y` and indeed it was different with calls to lambda with different types, and it was the same address for calls with arguments of the same type. I knew about `static` variables and also about templates, but I didn't fully understand this behaviour. Now I do, thanks a lot!! – J L Feb 23 '17 at 09:22
2

Lamda with auto is nothing more than a class with operator () overloaded to be a template and using auto for type deduction (which follows the rules for a template parameter deduction).

In this case you have as many static y fields as many instantiations of this function object template operator.

The initialization is done like in hand written class which is the first time the function is triggered (the lambda in this case)

Adrian Lis
  • 647
  • 4
  • 20