Search code examples
c++lambdastaticc++14auto

Static Auto Variable in Generic Lambda in C++14


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++


Solution

  • 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.