Lambda "this" capture returns garbage

I implement my own class that provides lazy initialization of its member. And I came across weird capture behavior thisin lambda.

Here is an example that reproduces this error.

//Baz.h
#include <memory>
#include <functional>
#include "Lazy.hpp"

struct Foo
{
    std::string str;

    Foo() = default;
    Foo(std::string str) : str(str) {}
    Foo(Foo&& that) : str(that.str) {  }
};

class Baz
{
    std::string str;

    Lazy<std::unique_ptr<Foo>> foo;

public:
    Baz() = default;
    Baz(const std::string& str) : str(str)
    {
        //lazy 'this->foo' initialization. 
        //Is capturing of 'this' valid inside ctors???.
        this->foo = { [this] { return buildFoo(); } };
    }
    Baz(Baz&& that) : foo(std::move(that.foo)), str(that.str) { }

    std::string getStr() const
    {
        return this->foo.get()->str;
    }

private:
    std::unique_ptr<Foo> buildFoo()
    {
        //looks like 'this' points to nothing here.
        return std::make_unique<Foo>(str); //got error on this line
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    ///Variant 1 (lazy Foo inside regular Baz):
    Baz baz1("123");
    auto str1 = baz1.getStr();

    ///Variant 2 (lazy Foo inside lazy Baz):
    Lazy<Baz> lazy_baz = { [](){ return Baz("123"); } };
    auto& baz2 = lazy_baz.get(); //get() method returns 'inst' member (and initialize it if it not initialized) see below
    auto str2 = baz2.getStr();

    return 0;
}

Option 1 works well.

Option 2 fails:

Unhandled exception in 0x642DF4CB (msvcr120.dll) in lambda_this_capture_test.exe: 0xC0000005: location of read access violation 0x00E0FFFC.

I am using the vC ++ 120 compiler (from VS2013).

Here is my simplified class Lazy:

#pragma once
#include <memory>
#include <atomic>
#include <mutex>
#include <functional>
#include <limits>

template<
    class T,
        typename = std::enable_if_t<
        std::is_move_constructible<T>::value &&
        std::is_default_constructible<T>::value
        >
>
class Lazy
{
    mutable std::unique_ptr<T> inst;
    std::function<T(void)> func;
    mutable std::atomic_bool initialized;
    mutable std::unique_ptr<std::mutex> mutex;

public:
    Lazy()
        : mutex(std::make_unique<std::mutex>())
        , func([]{ return T(); })
    {
        this->initialized.store(false);
    }

    Lazy(std::function<T(void)> func)
        : func(std::move(func))
        , mutex(std::make_unique<std::mutex>())
    {
        this->initialized.store(false);
    }
//... <move ctor + move operator>
    T& get() const
    {
        if (!initialized.load())
        {
            std::lock_guard<std::mutex> lock(*mutex);

            if (!initialized.load())
            {
                inst = std::make_unique<T>(func());
                initialized.store(true);
            }
        }

        return *inst;
    }
};

So my question is: why is this example more beautiful? Is it really for capturing thisinside constructors?

+4
2

, this . , , this . this .

, . Baz, this , , main - lambda ( return Baz("123"). , a Baz Lazy<Baz>, std::function Baz Baz, Lazy<Baz>::inst, this , , , Baz. wham, .

Donghui Zhang ( enable_shared_from_this shared_ptr this) . Lazy<T> T, std::unique_ptr<T>. std::function<std::unique_ptr<T>()>, , , , , , Lazy, this .

+8

, this - . this, . this , .

; , , .

Lazy. Lazy , T.

.

template<
  class Sig, class=void
>
class Lazy;

template<
  class T,
  class...Sources
>
class Lazy<
  T(Sources...),
  std::enable_if_t<
    std::is_move_constructible<T>::value &&
    std::is_default_constructible<T>::value
  >
>
{
  std::function<T(Sources...)> func;
  // ...
  Lazy(std::function<T(Sources...)> func)
  // ...
  T& get(Sources...srcs) const {
  // ...
            inst = std::make_unique<T>(func(std::forward<Sources>(srcs)...));
  // ...

Baz

Lazy<std::unique_ptr<Foo>(Baz const*)> foo;

ctor getStr:

Baz(const std::string& str) : str(str)
{
    this->foo = { [](Baz const* baz) { return baz->buildFoo(); } };
}

std::string getStr() const
{
    return this->foo.get(this)->str;
}

main , Baz :

Lazy<Baz()> lazy_baz = { []{ return Baz("123"); } };
0

Source: https://habr.com/ru/post/1657758/


All Articles