在可移动类型的构造函数lambda中安全使用捕获的此值

时间:2019-01-24 20:12:26

标签: c++ lambda

使用此人为设计的代码,我们需要在构造器中创建一个lambda,以捕获可移动类型的this

#include <functional>
#include <string>
#include <iostream>

using namespace std::string_literals;

namespace
{
class foo
{
public:
    template <typename T>
    explicit foo(std::string key, T val) :
        key_{std::move(key)}
    {
        f_ = [this, v = std::move(val)]() {
            std::cout << key_ << ": " << v << std::endl;
        };
    }

    void print()
    {
        f_();
    }

private:
    std::function<void ()> f_;
    std::string key_;
};
}

int main()
{
    auto f1 = foo("hello", "goodbye"s);
    auto f2 = std::move(f1);
    f2.print();

    return EXIT_SUCCESS;
}

在此示例中,我们使用lambda进行类型擦除,但这并不重要-重要的是我们在构造函数的lambda中捕获了this

Running this将产生:

: goodbye

(我认为)这是因为正在打印的key_成员是移出的f1::key_的空外壳,即捕获的this指针仍指向{{1} },尽管现在位于f1之内。

我可以想到几种针对具体情况的(笨拙的)方式,但是有标准/通用/更好/等等。 lambda内部有效引用拥有实例的方法?

1 个答案:

答案 0 :(得分:1)

一个简单的解决方案可能是避免捕获this并将其作为参数提供给函数对象。这样,您就可以自由地复制和移动功能对象,而不必考虑哪个实例拥有哪个功能对象。当需要调用该函数时,当前所有者可以将自身传递给该函数。

例如,这是您原始示例的外观:

#include <iostream>
#include <functional>
#include <string>

class foo
{
public:
    template <typename T>
    explicit foo(std::string key, T val) :
        key_{ std::move(key) }
    {
        f_ = [v = std::move(val)](foo * const this_ptr) {
            std::cout << this_ptr->key_ << ": " << v << std::endl;
        };
    }

    void print()
    {
        f_(this);
    }

private:
    std::function<void(foo *)> f_;
    std::string key_;
};