【问题标题】:C++11 lambdas: member variable capture gotchaC++11 lambdas:成员变量捕获陷阱
【发布时间】:2011-10-14 15:57:51
【问题描述】:

考虑这段代码:

#include <memory>
#include <iostream>

class A
{
public:
    A(int data) : data_(data)
    { std::cout << "A(" << data_ << ")" << std::endl; }
    ~A() { std::cout << "~A()" << std::endl; }
    void a() { std::cout << data_ << std::endl; }
private:
    int data_;
};

class B
{
public:
    B(): a_(new A(13)) { std::cout << "B()" << std::endl; }
    ~B() { std::cout << "~B()" << std::endl; }
    std::function<void()> getf()
    {
        return [=]() { a_->a(); };
    }
private:
    std::shared_ptr<A> a_;
};

int main()
{
    std::function<void()> f;
    {
        B b;
        f = b.getf();
    }
    f();
    return 0;
}

这里看起来我正在按值捕获a_ 共享指针,但是当我在 Linux (GCC 4.6.1) 上运行它时,会打印出来:

A(13)
B()
~B()
~A()
0

显然,0 是错误的,因为 A 已经被破坏了。看起来this 实际被捕获并用于查找this-&gt;a_。当我将捕获列表从[=] 更改为[=,a_] 时,我的怀疑得到了证实。然后打印正确的输出并且对象的生命周期与预期的一样:

A(13)
B()
~B()
13
~A()

问题:

此行为是由标准指定的、实现定义的还是未定义的?还是我疯了,完全不同?

【问题讨论】:

  • 我认为按照标准,a_ 通常会解析为 this->a_,除非您明确告诉它复制 a_。
  • 对我来说似乎完全合法 - 范围内的唯一变量是 this。如果一个指针只是神奇地解除了成员引用,那将是令人惊讶的!不过,这是一个很好的问题,也是对孩子们的一个很好的警告,不要鲁莽地使用惰性捕获 [=]/[&amp;]

标签: c++ c++11


【解决方案1】:

标准是否规定了这种行为

是的。捕获成员变量总是通过捕获this 来完成;这是访问成员变量的唯一方法。在成员函数的范围内a_ 等价于(*this).a_。在 Lambda 中也是如此。

因此,如果您使用this(隐式或显式),则必须确保对象在 lambda 实例存在时保持活动状态。

如果你想按值捕获它,你必须明确地这样做:

std::function<void()> getf()
{
    auto varA = a_;
    return [=]() { varA->a(); };
}

如果您需要规格报价:

lambda 表达式的复合语句产生函数调用运算符的函数体 (8.4),但出于名称查找 (3.4) 的目的,确定 this (9.3.2) 的类型和值并转换 id-使用 (*this) (9.3.1) 将非静态类成员的表达式引用到类成员访问表达式中, 在 lambda 表达式的上下文中考虑复合语句。

【讨论】:

  • 恕我直言,lambda 最可怕的陷阱之一。我只是希望所有编译器最终都会为此添加一个大警告。
  • @Martin:你为什么要警告这个?如果 lambda 在算法内部使用,那将非常好。他只是遇到了一个问题,因为他正在 返回 lambda 给某人。
  • @Dani 但将shared_ptr 返回给成员并不是错误。这似乎是一个潜在的常见错误,因为我在转换现有代码时遇到了它。我确实认为有一个警告(至少在 -Wextra 上),例如,当您通过 this-&gt; 显式访问该成员或将其添加到捕获列表时,可能会被静音。
  • @Nicol:对我来说,这里的问题不是讨厌的老 C++ 让我在脚下开枪,问题是 lambda 的“按值捕获”默默地捕获 this 而不是比_a。这值得一个可选的警告,IMO,无论是否返回 lambda。如果警告鼓励您明确地写(*this)._a,那么很明显它是this 被捕获。编译器会警告其他不太危险和不太正确的事情。
  • @Nicol:我不想“惩罚”任何人,我认为可选的警告是合理的。你不必启用它,所以我发现你如此热情以至于它甚至不应该提供给其他人,这让我感到惊讶。您会发现 [=] 捕获 this 而不是 _a 很明显,因为您完全熟悉该领域的标准。这很好,但编译器会警告该语言的微妙之处要少得多。
猜你喜欢
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-29
  • 2014-12-08
相关资源
最近更新 更多