Безопасное использование захваченного в конструкторе лямбда в подвижных типах - PullRequest
0 голосов
/ 24 января 2019

Возьмите этот придуманный код, где нам нужно создать лямбду в конструкторе, который захватывает 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;
}

В этом примере мы используем лямбду для стирания типа, но это действительно не имеет значения - все, что важно, это то, что мы фиксируем this в лямбду в конструкторе.

Запуск этого даст:

: goodbye

Это (я думаю) потому, что печатаемый элемент key_ является пустой оболочкой перемещенного из f1::key_, то есть захваченный указатель this все еще указывает на f1, несмотря на то, что теперь находится внутри f2 .

Я могу придумать несколько (неуклюжих) способов обойти это, которые зависят от обстоятельств, но есть ли стандарт / общий / лучше / и т.д. способ эффективного обращения к экземпляру-владельцу изнутри лямбды?

1 Ответ

0 голосов
/ 25 января 2019

Простое решение может состоять в том, чтобы избежать захвата 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_;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...