为什么成员函数尝试块处理程序中的 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?

this question about static initializers 不同但可能相关。

前两个函数编译正常,最后一个函数在 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;
    }();
}

我在标准中找不到任何建议 function-try-block 的处理程序/catch 块应该从函数范围中豁免或者 lambda 的闭包类型应该改变的建议。如果访问类型更改为所有 public.

,代码将编译

根本原因可能是什么?它是错误,还是特定于可以更改的编译器设置?

我猜这是一个编译器错误。它在 VS2015 中也报告相同的错误。奇怪的是,尝试显式模拟 lambda 的功能在 VS2015 中没有任何问题

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++ 编译器在幕后有什么不同...

这似乎是一个错误,catch 范围内的 lambda 是在 class 范围外生成的。我试图用 typeids 来证明这一点,但是 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)'