【问题标题】:c++: why a specific parameter must be captured by value in lambdac ++:为什么必须通过lambda中的值捕获特定参数
【发布时间】:2018-07-09 20:55:01
【问题描述】:
#include <iostream>
#include <functional>
#include <utility>

using namespace std;

typedef std::function<void(const string&, const string&, const bool, const bool)> 
    Callback_T;

class A {
public:
    void
    process(const string &a, const string &b, const bool c, const bool d, const int e)
    {
        cout << "a: " << a << " b: " << b << " c: " << c << " d: " << d << " e: " << e << endl;
    }

    Callback_T
    constructCallback(const int &e)
    {
        Callback_T callback =
        [&, this, e](auto&&...args) // <--- here, e must be captured by value, why?
        {
            this->process(
            std::forward<decltype(args)>(args)...,
            e);
        };

        return callback;
    }
};

int main()
{
    A a;
    auto cb = a.constructCallback(20);
    cb("A", "B", true, false);
}

上述程序输出:“a:A b:B c:1 d:0 e:20” 但是,如果我将捕获 e 的那条线更改为:

[&, 这个, &e]

它输出:“a: A b: B c: 1 d: 0 e: 26340408”,似乎表明 e 没有定义/初始化。

为什么只通过价值来捕捉它?

【问题讨论】:

标签: c++ c++11 lambda c++14


【解决方案1】:

你所拥有的是一个悬空参考。由于e 是一个引用参数,它绑定到其他东西。在这种情况下,它是从文字 20 创建的临时对象。当函数结束离开 callback 并引用不再存在的对象时,此临时值超出范围。

当您按值捕获时,您会否定此问题,因为 lambda 将存储它自己的 e 副本,确保它在 constructCallback 返回后仍然有效。


当通过引用捕获时,您必须确保没有路径会留下对不存在的东西的引用。

【讨论】:

    【解决方案2】:

    因为调用函数时e 超出范围,并且您有一个悬空引用。在以下几行中:

    A a;
    auto cb = a.constructCallback(20);
    cb("A", "B", true, false);
    

    当调用constructCallback(20) 时,会创建一个值为20 的本地e,它通过引用您的lambda 来捕获,然后在函数退出时销毁。因此,使用此引用是未定义的行为,并导致您观察到垃圾值。如果您改为按值捕获e,它会被复制,并且与 lambda 一样长。如果您确实需要通过引用使用e,则需要确保在评估lambda之前它没有被破坏。

    【讨论】:

    • 所以即使我将e 更改为按值传递:constructCallback(const int e),它仍然是临时的,并且在评估回调之前仍然会超出范围,对吗? (但这样做似乎是打印 20,为什么?)
    • @LeiMao 在这种情况下,您仍在绑定对函数参数的引用,该引用在函数返回后将超出范围。它仍然是未定义的行为,恰好20 尚未在曾经属于e 的内存中被覆盖。这仍然非常糟糕,如果您希望程序以可预测的方式运行,则需要避免。
    • 您可以将您的函数更改为constructCallback(int&amp; e) 并传递一个变量而不是像20 这样的临时文字。在这种情况下,您需要确保在调用回调时该变量仍然存在,并且出于上述所有相同的原因。这会给你带来的唯一好处是改变变量会改变回调看到的值。如果你愿意,这取决于你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多