为什么成员函数尝试块处理程序中的 lambda(捕获"this")无法访问 VC++ 2013 中的私有数据成员?

Why can't a lambda (capturing 'this') in a member function-try-block handler access private data members in VC++ 2013?

本文关键字:访问 VC++ 数据成员 2013 this 捕获 函数 成员 处理 lambda 程序      更新时间:2023-10-16

与静态初始化器的问题不同,但可能与此有关。

在这里,前两个函数可以很好地编译,最后一个函数在vc++中不能编译,但在clang和gcc中可以:

class A {
protected:
    std::string protected_member = "yay";
public:
    void withNormalBlock();
    void withFunctionBlock();
    void noLambda();
};

void A::withNormalBlock() {
    try {
        throw std::exception();
    } catch (...) {
        [this]() {
            std::cout << protected_member << std::endl;
        }();
    }
}
void A::noLambda() try {
    throw std::exception();
} catch (...) {
    std::cout << protected_member << std::endl;
}
void A::withFunctionBlock() try {
    throw std::exception();
} catch (...) {
    [this]() {
        // this line is the problem:
        std::cout << protected_member << std::endl;
    }();
}
  • in clang (OK)
  • gcc (OK)
  • in vc++ (error C2248: 'A::protected_member' : cannot access protected member declared in class 'A')
  • vc++ 2015—相同的处理

我在标准中找不到任何建议function-try-block的handler/catch块应该从函数范围中豁免,或者lambda的闭包类型应该改变。如果将访问类型更改为全public,则代码将编译。

根本原因是什么?这是一个bug,还是可以更改编译器设置的特定内容?

我猜这是一个编译器错误。它在VS2015中也会报告相同的错误。奇怪的是,在VS2015

中,显式模拟lambda功能的尝试没有任何问题。
class A
{
protected:
  std::string protected_member = "yay";
public:
  void withFunctionBlock();
};
void A::withFunctionBlock() try
{
  throw std::exception();
}
catch (...)
{
  struct Closure { 
    Closure(A *this_) : this_(this_) {}
    void operator ()() const { std::cout << this_->protected_member << std::endl; }
    A *this_;
  };
  Closure(this)();
}

我想知道VS c++编译器在底层有什么不同…

似乎这是一个bug, catch作用域中的lambda是在类作用域之外生成的。我试图用类型来证明这一点,但是Visual Studio的lambda名称被奇怪地弄乱了,名字本身并不能证明任何东西。但是,以下代码段生成的错误代码显示名称不同:

#include <iostream>
#include <typeinfo>
class Foo {
private:
public:
    void testLambda()
    try {
        auto tryScope = [this]() {};
        void (*p)() = tryScope;
    }
    catch(...)
    {
        auto catchScope = [this]() {};
        void (*p)() = catchScope;
    }
};
错误输出:

(10): error C2440: 'initializing' : cannot convert from 'Foo::testLambda::<lambda_8a3a8afea7359de4568df0e75ead2a56>' to 'void (__cdecl *)(void)' (15): error C2440: 'initializing' : cannot convert from '<lambda_8cbc08e7748553fb5ae4e39184491e92>' to 'void (__cdecl *)(void)'