Моя лямбда не правильно конвертирует захваченное «это» во время создания копии - PullRequest
3 голосов
/ 26 июня 2019

Я сузил свою проблему именно до этого

#include <iostream>
#include <functional>

struct Foo {
    std::function<Foo*()> lambda;
    Foo()
        :lambda([this](){return this;})
    {}

};

int main(){

    Foo a;
    Foo b = a; 
    std::cout << &a << " " << a.lambda() << std::endl;
    std::cout << &b << " " << b.lambda() << std::endl;
}

где вывод

0x7ffd9128b8a0 0x7ffd9128b8a0
0x7ffd9128b880 0x7ffd9128b8a0

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

Есть ли способ исправить это, чтобы лямбда всегда имела ссылку на свой объект-владелец this даже при копировании конструкции объекта-владельца.

Ответы [ 2 ]

9 голосов
/ 26 июня 2019

Звучит так, будто вам нужно предоставить свои собственные специальные функции-члены, нет? Например, для конструктора копирования:

Foo(const Foo& other)
   :lambda([this](){return this;})
{}
1 голос
/ 26 июня 2019

Пока @lubgr ответил на вопрос, на который я спросил, я думаю, что стоит отметить другое решение, которое у меня есть для моей конкретной проблемы.Вопрос возник из-за создания класса для инкапсуляции ленивой инициализации членов.Моя первоначальная попытка была

template <typename T>
class Lazy {
    mutable boost::once_flag _once;
    mutable boost::optional<T> _data;
    std::function<T()> _factory;
    void Init() const { boost::call_once([&] { _data = _factory(); }, _once); }
public:
    explicit Lazy(std::function<T()> factory):_once(BOOST_ONCE_INIT),_factory(factory){}

    T& Value() {
        Init();
        return *_data;
    }

}; 

, которая может быть использована как

class Foo { 
    int _a;
    Lazy<int> _val;
    Foo(a):_a(a):_val([this](){return this->_a+1;}){}
}

Foo f(10);
int val = f._val.Value();

, но имеет ту же проблему, которую я задал в своем вопросе, в том, что this является круговой ссылкой, которая не 'сохраняются для копирования конструкции.Решение не в том, чтобы создать собственный конструктор копирования и, возможно, в перемещении конструктора, а в том, чтобы исправить класс реализации Lazy, чтобы мы могли передать arg фабрике.

Новая реализация Lazy для членов:

template <typename T, typename TThis>
class LazyMember {
    mutable boost::once_flag _once;
    mutable boost::optional<T> _data;
    typedef std::function<T(TThis const*)> FactoryFn;
    FactoryFn _factory;
    void Init(TThis const * arg0) const { boost::call_once([&] { _data = _factory(arg0); }, _once); }
public:
    explicit LazyMember(FactoryFn factory):_once(BOOST_ONCE_INIT),_factory(factory){}

    T& Value(TThis const * arg0) { Init(arg0); return *_data; }
    T const & Value(TThis const * arg0) const { Init(arg0); return *_data; }
}; 

, который используется как

class Foo { 
    int _a;
    Lazy<int> _val;
    Foo(a):_a(a):_val([](Foo const * _this){return _this->_a+1;}){}
}

Foo f(10);
int val = f._val.Value(&f);

, и у него нет проблем с циклической ссылкой, и, следовательно, не требуется пользовательский конструктор копирования / перемещения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...