static_cast этого в другой базовый класс

Является ли следующее использование static_cast в конструкторе «безопасным»?

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

Думаю, я прав, говоря, что Dispatcher_Base гарантированно создается до Message_Type_1 и Message_Type_2, поэтому при вызове их конструкторов static_cast вернет мне действительный объект из "этого". Однако он довольно плохо пахнет, поэтому мне интересно, не допустил ли я огромную ошибку в основной предпосылке.

#include <iostream>
#include <vector>

template<typename dispatcher_type>
class Message_Type_1
{
public:

    Message_Type_1()
    {
        auto * dispatcher = static_cast<dispatcher_type *>(this);

        dispatcher->Add_Handler(1);
    }

    virtual ~Message_Type_1() = default;
};

template<typename dispatcher_type>
class Message_Type_2
{
public:

    Message_Type_2()
    {
        auto * dispatcher = static_cast<dispatcher_type *>(this);

        dispatcher->Add_Handler(2);
    }

    virtual ~Message_Type_2() = default;
};

class Dispatcher_Base
{
public:

    Dispatcher_Base()
    {

    }

    void Add_Handler(int x)
    {
        listeners.push_back(x);
    }

    std::vector<int> const & Get_Listeners() const
    {
        return listeners;
    }

    virtual ~Dispatcher_Base() = default;

private:

    std::vector<int> listeners;
};

class Dispatcher_Derived :  public Dispatcher_Base,
                            public Message_Type_1<Dispatcher_Derived>,
                            public Message_Type_2<Dispatcher_Derived>
{
public:

    Dispatcher_Derived() = default;

    virtual ~Dispatcher_Derived() = default;
};

int main(void)
{
    Dispatcher_Derived dispatcher;

    auto const & listeners = dispatcher.Get_Listeners();

    std::cout << "There are " << listeners.size() << " listeners in the set." << std::endl;

    for (auto i : listeners)
    {
        std::cout << i << std::endl;
    }

    return 0;
}

person Robinson    schedule 05.02.2018    source источник
comment
Вы хотели наследовать от Message_Type_1<Dispatcher_Base> вместо Message_Type_1<Dispatcher_Derived> в вашем примере?   -  person nwp    schedule 05.02.2018
comment
Ну, вы могли бы так подумать (и я действительно сначала подумал), но на самом деле это дает мне ошибку компиляции. Это работало с Dispatcher_Derived, поэтому я использовал его, не совсем понимая, почему.   -  person Robinson    schedule 05.02.2018
comment
Ну, поскольку код написан сейчас, static_cast определенно является UB, потому что в конструкторе Message_Type_1 определенно еще не существует объекта типа Dispatcher_Derived, потому что для создания Dispatcher_Derived вы должны сначала создать Message_Type_1.   -  person nwp    schedule 05.02.2018
comment
Но Dispatcher_Base существует, и именно здесь можно найти функцию Add_Handler. Помимо запаха кода, мне интересно, что он работает нормально как в GCC, так и в MSVC.   -  person Robinson    schedule 05.02.2018
comment
Это не запах кода, это неопределенное поведение. И вам нужно немного растянуть определение работы в GCC, потому что если вы запустите его с -fsanitize=undefined,address, он выдаст runtime error: downcast of address 0x7fffffffe540 which does not point to an object of type 'Dispatcher_Derived'.   -  person nwp    schedule 05.02.2018
comment
Хорошо, я убежден. Спасибо.   -  person Robinson    schedule 05.02.2018