17

给定以下示例代码:

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 都没有发出任何警告。

我对 lambdas 的理解是编译器会生成一个仿函数类。该仿函数类将具有所有复制变量的成员(i在示例中)。我相信在我的代码的上下文中,只要f存在,返回其成员之一的地址是安全的。在我看来,所有编译器都弄错了。我认为在超出范围后使用f' 成员地址的警告是有效的,但有关“局部变量 'i'”的警告是不正确/具有误导性的。我对吗?if

4

2 回答 2

7

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

演示很简单:只需修改您的示例以输出地址。

#include <iostream>

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

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

http://ideone.com/OqsDyg

顺便说一句,这-Wall在 GCC 4.9 下编译时没有警告。

于 2013-09-22T23:42:29.860 回答
4

一些术语:

  • =or内部称为capture- default &[&](){ /*..*/ }
  • 变量的 odr-use 粗略意味着该变量不会出现在废弃值表达式(例如(void)some_variableor int x = some_variable, 5;)中,也不会出现在常量表达式中。
  • 复合语句是“功能块”{ 语句 }
  • 变量的名称是一个id 表达式

[expr.prim.lambda]/3

lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。

/11

如果lambda 表达式具有关联的捕获默认值及其复合语句odr-uses (3.2)this或具有自动存储持续时间的变量,并且未显式捕获使用 odr 的实体,则称使用 odr 的实体是隐式捕获

因此,i被隐式捕获。

/14

如果实体被隐式捕获并且默认捕获是,=或者如果使用不包含&. 对于通过副本捕获的每个实体,在闭包类型中声明了一个未命名的非静态数据成员。

闭包类型中有一个非静态数据成员(类型为int)。

/17

复制捕获的实体的 odr-use (3.2) 的每个id 表达式都转换为对闭包类型的相应未命名数据成员的访问。

我们甚至不需要解释这一点,因为本段为我们提供了一个与 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
    };
}

如果我们将此应用于 OP 的示例,我们会看到它&i指的是闭包类型的内部非静态数据成员。标准中未指定诊断消息是否合适;)

于 2013-09-23T00:12:39.633 回答