'Why lambda in static initializer can't access private members of class in VC++2013?

Consider the following piece of code:

#include <iostream>

class foo {
  int var = 99;
public:
  static int const i;
};

int const foo::i = [&] { return foo().var; }();

auto main() -> int {
  std::cout << foo::i << std::endl;
  return 0;
}

Considering the standard § 9.4.2/2 Static data members [class.static.data]:

The initializer expression in the definition of a static data member is in the scope of its class.

and

§ 5.1.2/2&3 Lambda expressions [expr.prim.lambda]:

2 The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A closure object behaves like a function object (20.9).-end note]

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

We end up to conclusion that the lambda in the expression:

int const foo::i = [&] { return foo().var; }();

can rightfully access private members of class foo since it is declared and defined in the initializer expression of the static member i of class foo, and as such its scope is the scope of class foo.

The code compiles and runs fine in GCC v4.8 and Clang v3.4., however it fails to compile in VC++2013 producing a compiler error:

error C2248: 'foo::var' : cannot access private member declared in class 'foo'

Question:

  • Is the above recorded behaviour of VC++2013 a bug, or it is attribute to specific VC++2013 behaviour that could be altered by changing specific compiler settings?


Solution 1:[1]

It was a bug of Visual Studios up to 2017, and it is fixed in Visual Studio 2019. Demo: https://gcc.godbolt.org/z/4564EKbEr

And as to

int const foo::i = [&] { return foo().var; }();

non-local lambda expression cannot have a capture-default, so I changed it in the demo.

But as my colleague pointed me out, a similar bug was still present in Visual Studio 2019 for member templates:

#include <iostream>

class foo {
  int var = 99;
public:
  template<class>
  static int const i;
};

template<class>
int const foo::i = [] { return foo().var; }(); // fails in MSVC

auto main() -> int {
  std::cout << foo::i<int> << std::endl;
  return 0;
}

Demo: https://gcc.godbolt.org/z/oPscPo1eG

It was fixed in Visual Studio 2022 version 17.2. See https://developercommunity.visualstudio.com/t/Templated-static-initializer-cannot-acce/1620389

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1