lambda可以安全地返回复制变量的地址吗?

时间:2013-09-22 22:50:30

标签: c++ c++11 lambda

给出以下示例代码:

int main()
{
    int i;
    auto f = [=]()mutable->int*
    {
            return &i;
    };

    return 0;
}
  1. g ++ v.4.8.1警告“本地变量'i'的地址已返回”。
  2. Clang v.3.2(MacOS的Clang)警告说“堆栈内存的地址 与本地变量'i'相关联“。
  3. VS2012和VS2013 RC都没有发出任何警告。
  4. 我对lambdas的理解是编译器将生成一个仿函数类。该仿函数类将包含所有复制变量的成员(示例中为i)。我相信在我的代码环境中,只要存在f,就可以安全地返回其中一个成员的地址。在我看来,所有编译器都错了。我认为在f超出范围后使用i的成员f的地址的警告是有效的,但有关“本地变量'i'的警告不正确/误导。我是对的吗?

2 个答案:

答案 0 :(得分:7)

是的,你是对的。 &运算符正在应用于成员,而不是本地对象。

演示非常简单:只需修改示例即可输出地址。

#include <iostream>

int main() {
    int i;
    std::cout << & i << '\n';

    std::cout << [=]() mutable -> int * {
        return & i;
    } () << '\n';
}

http://ideone.com/OqsDyg

顺便说一句,这在GCC 4.9中的-Wall下没有任何警告。

答案 1 :(得分:4)

一些术语:

  • =内的&[&](){ /*..*/ }称为 capture-default
  • 变量的使用大致意味着变量不会出现在丢弃值表达式(例如(void)some_variableint x = some_variable, 5;)中,并且它不会出现在常量表达式中。
  • 复合语句是“功能块”{ 语句 }
  • 变量的名称是 id-expression

[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 }; } 引用闭包类型的内部非静态数据成员。是否在标准中指定了诊断消息是否合适;)