为什么静态初始值设定项中的 lambda 无法访问 VC++2013 中类的私有成员?

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

本文关键字:VC++2013 访问 成员 lambda 为什么 静态      更新时间:2023-10-16

考虑以下代码:

#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;
}

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

static数据成员的定义在其类的作用域中。

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

2 lambda表达式的求值结果是一个右值临时值(12.2)。这个临时对象称为闭包对象。lambda表达式不能出现在未求值的操作数中(第5条)。[注:闭包对象的行为类似于函数对象(第20.9条)。]端注)

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

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

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

可以正确访问class fooprivate成员,因为它是在class foostatic成员i的初始化表达式中声明和定义的,因此它的作用域是class foo的作用域。

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

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

问题:

  • 以上记录的vc++ 2013的行为是一个bug,或者它是属性到特定的vc++ 2013行为,可以通过改变特定的编译器设置来改变?

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

至于

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

非局部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;
}

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

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