Lambda捕获引用通过copy和decltype [英] Lambda capture reference by copy and decltype
问题描述
考虑简单的程序:
int i = 0;
int& j = i;
auto lambda = [=] {
std :: cout< & j<< std :: endl; // odr-use j
};
根据[expr.prim.lambda],closure成员变量 j
应该有 int
:
如果其被隐式捕获并且捕获默认是
的引用。=
,或者如果它被明确捕获,则通过复制捕获的捕获,其不是形式&标识符或&标识符初始化器。对于通过复制捕获的每个实体,在闭包类型中声明未命名的非静态数据成员。这些成员的声明顺序未指定。 此类数据成员的类型是相应捕获的实体的类型,如果实体不是对对象或引用类型
所以我打印的是一些 int
的地址与外部范围无关 i
或 j
。这一切都很好。但是,当我引入 decltype
:
auto lambda = [j ] {
std :: cout<< & j<< std :: endl;
static_assert(std :: is_same< decltype(j),int> :: value,!); //错误:!
};
无法编译,因为 decltype(j)
计算为 int&
。为什么? j 在该范围应该指向数据成员,应该不是?
作为一个相关的后续, lambda捕获代替一个 init捕获与 [j = j] {...}
,然后
而不是
int&
。为什么有区别?
名称查找在lambda表达式中工作的方式有点奇怪: id-expressions which参考由拷贝捕获的实体从捕获的实体的访问变换为对封闭类型的存储的数据成员的访问,但是只有当这些访问构成 odr- / em> 。注意,由于隐式捕获,如果没有odr使用,可能没有这样的数据成员。
decltype
不构成odr使用,因此它将始终引用捕获的实体(原始),而不是数据成员(副本)。
C ++ 11 [expr.prim.lamba] p17
每个id表达式都是 odr使用由
副本捕获的实体被转换为对闭包类型的相应未命名数据
成员的访问。
每次出现
decltype((x) )
其中x
是可能带括号的
id-expression
被视为如果x
被转换为访问闭包类型的相应
数据成员,如果x
是表示实体的odr使用。 [示例:void f3(){
float x,& r = X;
[=] {// x和r未捕获(在decltype操作数中的外观不是odr使用)
decltype(x)y1; // y1有类型float
decltype((x))y2 = y1; // y2有float const&因为这个lambda
//不是可变的,x是一个左值
decltype(r)r1 = y1; // r1 has type float& (转换不考虑)
decltype((r))r2 = y2; // r2 has type float const&
};
}
- 结束示例
b $ b
C ++ 14 init-captures 自从C ++ 14捕获实体
如果其被隐式捕获并且capture-default 是
=
,或者如果它被明确地捕获,则通过复制捕获不是&
标识符或&
形式的。 / 2756719 / tc> TC 已经指出,它们不捕获它们已经初始化的实体,而是一个也用于类型推导的虚拟变量[expr.prim.lambda] p11
一个 init-capture 的行为就像它声明并显式地捕获一个
形式的变量auto
init-capture;
,其声明区域是
< ...]
类型推导改变这个变量的类型,例如 char const [N]
- > char const *
,原始实体甚至可能没有类型。 [i = {1,2,3}] {}
。
在 Consider the simple program: According to [expr.prim.lambda], the closure member variable An entity is captured by copy if it is implicitly captured and the capture-default is So what I'm printing is the address of some That fails to compile because As a related followup, if the lambda capture were instead an init-capture with The way name lookup works inside lambda-expressions is a bit peculiar: id-expressions which refer to entities captured by copy are transformed from accesses to the captured entities to accesses to the stored data members of the closure type -- but only if these accesses constitute odr-uses. Note that due to implicit capture, if there's no odr-use, there is possibly no such data member. C++11 [expr.prim.lamba]p17 Every id-expression that is an odr-use of an entity captured by
copy is transformed into an access to the corresponding unnamed data
member of the closure type. and furthermore, p18 even displays this weird effect in an example: Every occurrence of — end example ]
The C++14 init-captures are also considered capture by copy, since C++14 [expr.prim.lambda]p15 An entity is captured by copy if it is implicitly captured and the
capture-default is However, as T.C. has pointed out, they do not capture the entity they've been initialized with, but rather a "dummy variable" which is also used for type deduction [expr.prim.lambda]p11 An init-capture behaves as if it declares and explicitly captures a
variable of the form " The type deduction alters the type of this variable, e.g. Therefore, the id-expression 这篇关于Lambda捕获引用通过copy和decltype的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! [j = j] {decltype(j)x;
中定义
j }
是指这个虚拟变量,其类型为 int
,而不是 int&
。 / p> int i = 0;
int& j = i;
auto lambda = [=]{
std::cout << &j << std::endl; //odr-use j
};
j
should have type int
:
=
or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.int
unrelated to the outer-scope i
or j
. This is all well and good. However, when I throw in decltype
:auto lambda = [j] {
std::cout << &j << std::endl;
static_assert(std::is_same<decltype(j), int>::value, "!"); // error: !
};
decltype(j)
evaluates as int&
. Why? j
in that scope should refer to the data member, should it not?[j=j]{...}
, then clang would report decltype(j)
as int
and not int&
. Why the difference? decltype
does not constitute an odr-use, hence it will always refer to the captured entity (the original), not the data member (the copy).
decltype((x))
where x
is a possibly parenthesized
id-expression that names an entity of automatic storage duration is
treated as if x
were transformed into an access to a corresponding
data member of the closure type that would have been declared if x
were an odr-use of the denoted entity. [ Example: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&
};
}
=
or if it is explicitly captured with a capture
that is not of the form &
identifier or &
identifier initializer.
auto
init-capture ;
" whose declarative region is
the lambda-expression’s compound-statement [...]char const[N]
-> char const*
, and the original entity might not even have a type, e.g. [i = {1,2,3}]{}
.j
in the lambda [j=j]{ decltype(j) x; }
refers to this dummy variable and its type is int
, not int&
.