Использование t (* this) приводит к RuntimeError, тогда как t (std:: ref (* this) не

У меня есть следующий пример:

#include <iostream>
#include <functional>

struct Tmr {
    typedef std::function<void(void)> Callback;
    Callback cb;

    Tmr(Callback cb_) :
        cb( cb_ )
    {
    }

    void timeout()
    {
        cb();
    }
};

struct Obj {
    struct Tmr t;

    Obj() :
        t( std::ref( *this ) )
    {
    }

    void operator () ()
    {
        std::cout << __func__ << '\n';
    }
};

int main(int argc, char *argv[])
{
    Obj o;

    o.t.timeout();

    return 0;
}

Это работает отлично, но изначально у меня был конструктор Obj как:

Obj() :
    t( *this )

Это приводит к ошибке выполнения. Я предполагаю, что это связано с тем, что в моем обратном вызове хранится только ссылка на функцию-член, а не на объект для вызова элемента.

Я не понимаю, что делает std::ref, когда я делаю Obj() : t(std::ref(*this)) и почему это заставляет программу работать. Может ли кто-нибудь пролить свет на то, что происходит и как оно работает?

Ответ 1

Если вы не проходите по ссылке, вы копируете *this до того, как t был инициализирован - это означает, что вы копируете t и его элемент обратного вызова до того, как они были инициализированы, что составляет undefined.

(И конструктор копирования std::function, скорее всего, попытается скопировать то, на что указывает неинициализированный указатель, что и вызывает фактический сбой.)

Ответ 2

Сбой кода вызывается из-за копирования неинициализированного объекта обратного вызова. Вы можете увидеть последовательность событий ниже:

1. Copy constructor of Obj is called in t(*this)
2. Copy constructor of Tmr is called as t is a member of Obj
3. Copy constructor of Callback is called as cb is a member of Tmr
4. Execution fails while trying to copy from uninitialized Callback object.

Используя std:: ref, вы обойдете создание копии Obj; почему он не падает.