为什么静态初始化程序中的lambda无法访问VC++ 2013中的类的私有成员?

101*_*010 5 c++ lambda visual-c++ c++11 visual-studio-2013

考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

考虑标准§9.4.2/ 2静态数据成员[class.static.data]:

static数据成员定义中的初始化表达式属于其类的范围.

§5.1.2/ 2&3 Lambda表达式[expr.prim.lambda]:

2lambda表达式的评估导致prvalue临时(12.2).这个临时对象称为闭包对象.lambda表达式不应出现在未评估的操作数中(第5条).[注意:闭包对象的行为类似于函数对象(20.9).-结束注释]

3lambda-expression的类型(也是闭包对象的类型)是一个唯一的,未命名的nonunion类类型 - 称为闭包类型 - 其属性如下所述.此类类型不是聚合(8.5.1).闭包类型在包含相应lambda表达式的最小块作用域,类作用域或命名空间作用域中声明.

我们最终得出结论:表达式中的lambda:

int const foo::i = [&] { return foo().var; }();
Run Code Online (Sandbox Code Playgroud)

可以正确地访问private成员,class foo因为它是在static成员的初始化表达式中声明和定义iclass foo,因此其范围是其范围class foo.

代码在GCC v4.8Clang v3.4中编译并运行良好,但它无法在VC++ 2013中编译产生编译器错误:

错误C2248:'foo :: var':无法访问类'foo'中声明的私有成员

题:

  • 以上记录的VC++ 2013行为是一个错误,还是属于特定的VC++ 2013行为,可以通过更改特定的编译器设置来改变?

Fed*_*dor 0

这是 Visual Studios 2017 年之前的一个错误,在 Visual Studio 2019 中已修复。演示: https: //gcc.godbolt.org/z/4564EKbEr

至于

int const foo::i = [&] { return foo().var; }();
Run Code Online (Sandbox Code Playgroud)

非本地 lambda 表达式不能有捕获默认值,因此我在演示中更改了它。

但正如我的同事指出的那样,Visual Studio 2019 中的成员模板仍然存在类似的错误:

#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;
}
Run Code Online (Sandbox Code Playgroud)

演示: https: //gcc.godbolt.org/z/oPscPo1eG

它已在 Visual Studio 2022 版本 17.2 中修复。请参阅https://developercommunity.visualstudio.com/t/Templated-static-initializer-cannot-acce/1620389