Запретить пользователю выводить из неверной базы CRTP

Я не могу придумать правильное название вопроса, чтобы описать проблему. Надеюсь, что подробности ниже объяснят мою проблему ясно.

Рассмотрим следующий код

#include <iostream>

template <typename Derived>
class Base
{
    public :

    void call ()
    {
        static_cast<Derived *>(this)->call_impl();
    }
};

class D1 : public Base<D1>
{
    public :

    void call_impl ()
    {
        data_ = 100;
        std::cout << data_ << std::endl;
    }

    private :

    int data_;
};

class D2 : public Base<D1> // This is wrong by intension
{
    public :

    void call_impl ()
    {
        std::cout << data_ << std::endl;
    }

    private :

    int data_;
};

int main ()
{
    D2 d2;
    d2.call_impl();
    d2.call();
    d2.call_impl();
}

Он будет компилироваться и запускаться через определениеD2 намеренно неправильно. Первый звонокd2.call_impl() выведет несколько случайных битов, которые ожидаются какD2::data_ не был инициализирован. Второй и третий вызовы будут все выводить100 дляdata_.

Я понимаю, почему он будет компилироваться и запускаться, поправьте меня, если я ошибаюсь.

Когда мы делаем звонокd2.call()вызов разрешен доBase<D1>::callи это будет разыгрыватьthis вD1 и позвонитьD1::call_impl, Так какD1 действительно производная формаBase<D1>, так что приведение в порядке во время компиляции.

Во время выполнения после броскаthisв то время как это действительноD2 объект обрабатывается так, как будто онD1и призыв кD1::call_impl будут изменены биты памяти, которые должны бытьD1::data_и вывод. В этом случае эти биты оказались гдеD2::data_ являются. Я думаю второйd2.call_impl() также должно быть неопределенное поведение в зависимости от реализации C ++.

Суть в том, что этот код, будучи намеренно неправильным, не будет давать никаких признаков ошибки пользователю. Что я действительно делаю в своем проекте, так это то, что у меня есть базовый класс CRTP, который действует как механизм диспетчеризации. Другой класс в библиотеке обращается к базовому классу CRTP & apos; интерфейс, скажемcall, а такжеcall будет отправлять вcall_dispatch которая может быть реализацией базового класса по умолчанию или реализацией производного класса. Все это будет хорошо работать, если пользователь определил производный класс, скажем,D, действительно происходит отBase<D>, Это вызовет ошибку времени компиляции, если она получена изBase<Unrelated> гдеUnrelated не является производным отBase<Unrelated>, Но это не помешает пользователю писать код, как указано выше.

Пользователь использует библиотеку, наследуя базовый класс CRTP и предоставляя некоторые подробности реализации. Конечно, есть и другие варианты дизайна, которые могут избежать проблемы неправильного использования, как указано выше (например, абстрактный базовый класс). Но давайте пока отложим их и поверьте мне, что мне нужен этот дизайн по какой-то причине.

Поэтому мой вопрос заключается в том, могу ли я как-то помешать пользователю написать неверный производный класс, как показано выше. То есть, если пользователь пишет производный класс реализации, скажемD, но он вывел его изBase<OtherD>затем возникает ошибка времени компиляции.

Одним из решений является использованиеdynamic_cast, Однако, это обширно, и даже когда это работает, это ошибка времени выполнения.

Ответы на вопрос(6)

Ваш ответ на вопрос