给出以下示例代码:
int main()
{
int i;
auto f = [=]()mutable->int*
{
return &i;
};
return 0;
}
我对lambdas的理解是编译器将生成一个仿函数类。该仿函数类将包含所有复制变量的成员(示例中为i
)。我相信在我的代码环境中,只要存在f
,就可以安全地返回其中一个成员的地址。在我看来,所有编译器都错了。我认为在f
超出范围后使用i
的成员f
的地址的警告是有效的,但有关“本地变量'i'的警告不正确/误导。我是对的吗?
答案 0 :(得分:7)
是的,你是对的。 &
运算符正在应用于成员,而不是本地对象。
演示非常简单:只需修改示例即可输出地址。
#include <iostream>
int main() {
int i;
std::cout << & i << '\n';
std::cout << [=]() mutable -> int * {
return & i;
} () << '\n';
}
顺便说一句,这在GCC 4.9中的-Wall
下没有任何警告。
答案 1 :(得分:4)
一些术语:
=
内的&
或[&](){ /*..*/ }
称为 capture-default 。(void)some_variable
或int x = some_variable, 5;
)中,并且它不会出现在常量表达式中。 {
语句 }
[expr.prim.lambda] / 3
lambda-expression 的类型(也是闭包对象的类型)是一个唯一的,未命名的非联合类类型 - 称为闭包类型 - 其属性如下所述。
/ 11
如果 lambda-expression 具有关联的 capture-default 及其复合语句 odr-uses(3.2){{1}或者具有自动存储持续时间的变量并且未明确捕获使用了odr的实体,然后使用odr使用的实体隐式捕获;
因此,隐式捕获this
。
/ 14
如果隐式捕获实体并且捕获默认值为
i
,或者使用不包含=
的捕获显式捕获实体,则通过副本捕获实体。对于由副本捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员。
闭包类型中有一个非静态数据成员(类型为&
)。
/ 17
每个 id-expression 是由副本捕获的实体的odr-use(3.2),它被转换为对闭包类型的相应未命名数据成员的访问。
我们甚至不需要解释这一点,因为这段为我们提供了一个非常类似于OP的例子:
int
如果我们将此应用于OP的示例,我们会看到void f(const int*);
void g() {
const int N = 10;
[=] {
int arr[N]; // OK: not an odr-use, refers to automatic variable
f(&N); // OK: causes N to be captured; &N points to the
// corresponding member of the closure type
};
}
引用闭包类型的内部非静态数据成员。是否在标准中指定了诊断消息是否合适;)