lambda中捕获变量的decltype():GCC bug和/或Clang bug?

Quu*_*one 11 c++ lambda decltype c++11 c++14

我已经检查了GCC buglistClang buglist,但还没有看到任何相关内容.

这个Wandbox链接显示了一些C++ 11/C++ 14代码的运行decltype(x)以及lambda捕获的decltype((x))各种代码x.GCC和Clang为此代码提供了不同的答案.其中哪一个,如果是,是正确的?

这是令人讨厌的片段:

// inside main()
int i = 42;
int &j = i;
[j=j](){
    static_assert(std::is_same<decltype(j), GCC(const) int>::value,""); // A
    static_assert(std::is_same<decltype((j)), const int&>::value,""); // B
}();
[=](){
    static_assert(std::is_same<decltype(j), int&>::value,""); // C
    static_assert(std::is_same<decltype((j)), CLANG(const) int&>::value,""); // D
}();
Run Code Online (Sandbox Code Playgroud)

哪里:

#ifdef __clang__
 #define CLANG(x) x
 #define GCC(x)
#else
 #define GCC(x) x
 #define CLANG(x)
#endif
Run Code Online (Sandbox Code Playgroud)

我相信在这两种情况下,实际捕获的东西(*)是一个(非常量)int初始化为一个j值的副本(也就是说,它i的值).由于lambda未标记mutable,operator()因此它将成为const成员函数.有了这些先决条件,让我们继续......

在线// A,GCC告诉我,显式初始捕获的decltype jconst int,当我几乎肯定它应该是int(每个Clang).

在线// B,两个编译器都同意这(j)是一个引用const int的左值(因为lambda没有标记mutable); 这对我来说非常有意义.

在线// C,两个编制者都同意这j是一个引用int&第2行声明的名称.这是5.1.2 [expr.prim.lambda]/19的结果,或者更确切地说,是因为事情的结果 -当-该子句-IS- -being调用的.在[=]lambda中,名称 j指的j是外部范围,但表达式 (j)指的(j)是如果j要捕获的话,它将存在.我不完全理解这是如何工作的或为什么它是可取的,但它确实存在.我愿意规定这不是任何编译器中的错误.

在线// D,Clang告诉我这(j)是一个引用const int的左值,而GCC告诉我它是一个引用非const int 的左值.我很确定Clang是对的,GCC是错的; decltype((j))无论j是隐式还是显式捕获都应该是相同的.

所以:

  • 我的解释是否正确(根据标准)?
  • C++ 11和C++ 14(和C++ 1z)之间的正确答案是否有变化?
  • // A// D在GCC两个错误?
  • 这些错误已经提交了吗?

(*) -其实没有什么技术上捕获由第二拉姆达,因为它不使用j任何计算环境.这就是为什么行// A// C给出不同的答案.但我不知道该-的事情,但─任何好的术语 -being-做-TO- j,所以我只是说"俘虏".

Bar*_*rry 6

我认为两个编译器对于(A)都是错误的,并且(D)的gcc是错误的.
我相信gcc对于(A)和(D)是错误的,而clang对于两者都是正确的.


[expr.lambda.prim]的相关部分是:

一个INIT-捕获的行为就好像它声明和明确地捕获形式的可变"自动初始化捕获 ;",其陈述区域是λ-表达化合物语句,不同之处在于:
-如果捕获是通过拷贝(见在下面),为捕获声明的非静态数据成员和变量被视为引用同一对象的两种不同方式,它具有非静态数据成员的生命周期,并且不执行额外的复制和销毁,

lambda 表达式复合语句中的每个id表达式(由副本捕获的实体的odr-use(3.2))将转换为对闭包类型的相应未命名数据成员的访问.

decltype(j)这不是一种使用j,因此不应考虑这种转变.因此,在这种情况下[=]{...},decltype(j)应该屈服int&.但是,在init-capture的情况下,行为就好像存在表单auto j = j;的变量,并且变量j引用相同的未命名的非静态数据成员而不需要这样的转换.所以在这种情况下[j=j]{...},decltype(j)应该产生该变量的类型 - 即int.绝对不是const int.那是一个错误.

下一个相关部分:

每次出现的decltype((x))where x都是一个可能带括号的id-expression,它命名一个自动存储持续时间的实体,被视为x转换为对闭包类型的相应数据成员的访问,如果x是odr使用的话,该成员将被声明.表示实体. [例如:

void f3() {
    float x, &r = x;
    [=] {                       // x and r are not captured (appearance in a decltype operand is not an odr-use)
        decltype(x) y1;         // y1 has type float
        decltype((x)) y2 = y1;  // y2 has type float const& because this lambda
                                // is not mutable and x is an lvalue
        decltype(r) r1 = y1;    // r1 has type float& (transformation not considered)
        decltype((r)) r2 = y2;  // r2 has type float const&
    }
}
Run Code Online (Sandbox Code Playgroud)

- 末端的例子]

该示例进一步说明了decltype(j)应该int&在隐式复制的情况下,并且还演示了decltype((j))被视为是否x已经声明的相应数据成员:int const&在两种情况下都是如此(因为lambda不是mutable并且j是左值).您的(C)和(d)的情况下精确地反映r1,r2声明中的示例.虽然这些例子不是规范性的,但肯定表明gcc因为有不同的行为而错了.