默认参数位置的Lambda无法访问好友成员.这是编译器错误吗?

N.E*_*.C. 6 c++ lambda visual-c++ c++11 visual-studio-2017

我正在尝试编写一个用超现代的奥术编码技术编写的程序.这些技术是如此先进,GCC和Clang工作,但Visual Studio 2017引发了错误.现在我想知道Visual Studio是否正确.

考虑以下程序:

#include <functional>
#include <iostream>

class A
{
public:
    A(int i) : foo(i) { }
private:
    int foo;

    friend class B;
};

class B
{
public:
    void printFooFromA(const A& a, std::function<void (const A&)> printer = [](const A& a) {
        std::cout << "a.foo is " << a.foo << std::endl;
    }) {
        printer(a);
    }
};

int main()
{
    A a(123);
    B b;
    b.printFooFromA(a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Visual Studio抛出错误C2248:'A :: foo':无法访问在类'A'中声明的私有成员.

错误的根源在printFooFromA的声明中:"printer"参数以lambda的形式给出一个默认值.在lambda中,它访问A :: foo.由于foo是私有的,因此只能在A或A的朋友中访问.

这是否是一个错误取决于lambda是否应该被视为A的朋友.Visual Studio说不,而GCC说是.C++标准是否指定了这个?

编辑添加:StackOverflow上存在关于lambda是否被视为类的朋友的问题,但这些问题都没有解决lambda处于默认参数位置以及这是否符合标准C++的情况.

M.M*_*M.M 3

根据 C++17 [class.friend]/2:

将类声明为友元意味着可以在友元类的基说明符和成员声明中访问授予友谊的类中的私有和受保护成员的名称。

声明一个类成员函数肯定是一个成员声明,​​并且 lambda 肯定在声明中,所以我将其解释为 lambda 具有友谊状态。

为了支持这一点,有 [expr.prim.lambda.closure]/2:

闭包类型在包含相应 lambda 表达式的最小块作用域、类作用域或命名空间作用域中声明。[注意:这决定了与闭包类型关联的命名空间和类的集合。]

因此 lambda 的闭包类型是在B的作用域中声明的,这意味着它B大致相当于:

class B
{
    struct lam
    {
        void operator()(const A& a)
        {
            std::cout << "a.foo is " << a.foo << std::endl;
        }
    };
public:
    void printFooFromA(const A& a, std::function<void (const A&)> printer = lam()) 
    {
        printer(a);
    }
};
Run Code Online (Sandbox Code Playgroud)

在 [class.friend]/2 中,它给出了一个具有相同布局的示例,表明这friend class X;意味着 的嵌套类X也是友元。

很明显,这是 MSVC 中的一个错误。