我已检查过GCC buglist和Clang buglist,但看不到任何相关内容。
This Wandbox link显示了一些C ++ 11 / C ++ 14代码,用于对lambdas捕获的各种decltype(x)
行使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
}();
其中:
#ifdef __clang__
#define CLANG(x) x
#define GCC(x)
#else
#define GCC(x) x
#define CLANG(x)
#endif
我相信在这两种情况下,实际捕获的东西(*)是一个(非常量)int
初始化为j
的副本值(也就是说,i
的值)。由于lambda未标记为mutable
,因此其operator()
将成为const
成员函数。有了这些先决条件,让我们继续......
在线// A
,GCC告诉我,显式初始捕获的j
的decltype是const int
,当我几乎肯定它应该是int
时(每个Clang)。
在// B
行上,两个编译器都同意(j)
是一个引用const int的左值(因为lambda没有标记为mutable
);这对我来说非常有意义。
在// C
行上,两个编译器都同意j
是一个引用第2行声明的int&
的名称。这是5.1.2 [expr.prim.lambda]的结果] / 19,或者更确切地说,当事件发生的时候 - 那个 - 子句 - 不是 - 被调用的结果。在[=]
lambda中,名称 j
引用外部作用域中的j
,但表达式 (j)
}指的是在(j)
被捕获时存在的j
。我不完全了解这是如何工作的或为什么它是可取的,但它确实存在。我愿意规定这不是任何编译器中的错误。
在// D
行,Clang告诉我(j)
是一个引用const int的左值,而GCC告诉我它是一个引用非const 的左值INT。我很确定Clang是对的,GCC是错的; <{1}}应该是相同的,无论是隐式还是显式捕获decltype((j))
。
所以:
j
和// A
都是错误吗? (*) - 事实上,第二个lambda在技术上捕获没有任何内容,因为它在任何评估的上下文中都不使用// D
。这就是为什么行j
和// A
给出了不同的答案。但我不知道任何好的术语 - -being-done-to - // C
,所以我只是说“捕获”。
答案 0 :(得分:6)
我认为两个编译器对于(A)是错误的,而gcc对于(D)是错误的。 击>
我认为gcc对于(A)和(D)是错误的,而clang对于两者都是正确的。
[expr.lambda.prim]的相关部分是:
init-capture 的行为就像它声明并显式捕获“auto init-capture ;”形式的变量一样 其声明区域是 lambda-expression 的复合语句,但以下情况除外:
- 如果捕获是通过复制(见下文),则为捕获声明的非静态数据成员和 变量被视为引用同一对象的两种不同方式,它具有生命周期 非静态数据成员,不执行其他复制和销毁,
和
lambda-expression 的复合语句中的每个 id-expression ,这是一个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))
每次出现x
是一个可能带括号的 id-expression ,它将自动存储持续时间的实体命名为x
被转换访问闭包类型的相应数据成员,如果x
是对所表示实体的使用,则会声明该成员。 [例如: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& } }
-end example]
该示例进一步说明隐式副本案例中decltype(j)
应为int&
,并且还表明decltype((j))
被视为x
是相应的数据成员已声明:在两种情况下都是int const&
(因为lambda不是mutable
而j
是左值)。您的(C)和(D)个案完全反映了示例中的r1
,r2
声明。虽然这些例子不是规范性的,但肯定表明gcc因为有不同的行为而错了。