Используйте std :: function в качестве функции-члена, которая захватывает `this`, и получает доступ к ней из скопированной лямбды после деструктора - PullRequest
0 голосов
/ 09 июня 2018

Flex Ferrum отправьте пример кода здесь (я думаю, что Минимальный, Полный и Проверяемый достаточно):

#include <iostream>
#include <functional>
using namespace std;

class Bang
{
public:
    Bang(int i = 0) : m_val(i)
    {
        m_foo = [this] {std::cout << m_val << std::endl;};
    }

    ~Bang()
    {
        m_val = -1;
    }

    void Foo()
    {
        m_foo();
    }
private:
    int m_val;
    std::function<void ()> m_foo;
};

Bang GetBang()
{
    return Bang(100500);
}

int main() {
    Bang b(100500);
    b.Foo();
    b = GetBang();
    b.Foo();
    return 0;
}

Наш хороший Flex также предлагает live demo


После грубого взгляда я подумал, что он выдаст 100500, но реальный результат -:

-1
  • Почему?Что за этим стоит?
  • Как это исправить? (Вывод 100500, а не -1)

Я написал несколько собственных рассуждений в ask box, но обнаружил, что это более подходит для публикации в качестве ответа (сделает вопрос слишком длинным).Если мой ответ неверный, пожалуйста, исправьте его, и больше ответов приветствуются

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Возможно, вы захотите передать текущий объект в лямбду со значением *this, чтобы он мог быть сохранен и скопирован при копировании назначить Bang.Передав указатель this, вы сохраните и скопируете указатель на временный объект, который был уничтожен при копировании назначить Bang.

Работает как надо:

#include <iostream>
#include <functional>

class Bang
{
public:
    Bang(int i = 0) : m_val(i)
    {
        m_foo = [bang = *this] { std::cout << bang.m_val << std::endl; };
    }

    ~Bang()
    {
        m_val = -1;
    }

    void Foo()
    {
        m_foo();
    }
private:
    int m_val;
    std::function<void()> m_foo;
};

Bang GetBang()
{
    return Bang(100500);
}

int main()
{
    Bang b;
    b = GetBang();
    b.Foo();
    return 0;
}

Демонстрация: https://ideone.com/LUDrBb

0 голосов
/ 09 июня 2018

Ах, он должен винить деструктор временного - Bang(100500), который возвращает форму GetBang, равен prvalue и имеет temporary object lifetime.

  1. [this] будет храниться как ссылка на *this, например:

    class Lambda
    {
    public:
        void operator()() const
        {
            //output
        }

    private:
        Bang& bang;

    public:
        Lambda(Bang& bang) : bang{bang}
        {
        }

    } lambda{*this};
    ...
    m_foo = lambda;

Поскольку здесь нет RVO, поэтому временный Bang(100500) сначала будет присвоен b, а затем уничтожен.

Custorm operator(), constructor и destructor для вывода некоторой информации:


#include <iostream>
#include <functional>

using namespace std;

class Bang
{
public:
    Bang(int i = 0) : m_val(i)
    {

        std::cout << "Bang(int i = 0) m_val address is " << &m_val << '\n';
        class Lambda
        {
        public:
            void operator()() const
            {

                std::cout << "operator() m_val address is " << &bang.m_val << '\n';
                std::cout << bang.m_val << std::endl;
            }

        private:
            Bang &bang;

        public:
            Lambda(Bang &bang) : bang{bang}
            {
            }

        } lambda{*this};
        m_foo = lambda;

    }

    ~Bang()
    {
        std::cout << "~Bang()\n";
        m_val = -1;
    }

    void Foo()
    {
        m_foo();
    }

private:
    int m_val;
    std::function<void()> m_foo;
};

Bang GetBang()
{
    return Bang(100500);
}

int main()
{
    Bang b;
    b = GetBang();
    b.Foo();
    return 0;
}

live demo

Выход:

Bang(int i = 0) m_val address is 0x7ffd202c48b0
Bang(int i = 0) m_val address is 0x7ffd202c48e0
~Bang()
operator() m_val address is 0x7ffd202c48e0
-1
~Bang()

показывает:

  • dtor будет вызыватьсяперед выводом, это означает, что временный объект был уничтожен.Адрес
  • m_value не меняется.

Двое гарантировали, что мы все еще получаем доступ к временному m_value из b m_foo().

Для доступа к объекту, который был уничтожен, должно быть Неопределенное поведение , но предупреждения и ошибки не требуются.

Обновление

Чтобы решить проблему, есть два решения:

  1. Как @ Killzone Kid указывает,захват с инициализатором: [bang = *this].Для этого требуется c ++ 14.
  2. Более простой способ захвата текущего объекта путем копирования: [*this].Это требует c ++ 17.live demo
...