CRTP с reinterpret_cast в целевой класс - PullRequest
0 голосов
/ 30 апреля 2018

Фон

Я бы хотел, чтобы фасады применялись по отдельности, а не запекали их в самом классе. Но мне нужно работать с данными, поэтому мне нужно, чтобы this был доступен с фасада. Вот небольшой пример:

#include <array>
#include <iostream>

template <typename T>
struct x_getter
{
    friend T;

    double x() const
    {
        return (*real_self)[0];
    }

    void x(double new_x)
    {
        (*real_self)[0] = new_x;
    }

private:
    T* real_self = reinterpret_cast<T*>(this);
    x_getter() = default; //prevents accidental creation
};

struct coordinates : std::array<double, 3>, x_getter<coordinates>
{
    using std::array<double, 3>::array;
};


int main()
{
    coordinates origin{};
    std::cout << origin.x();
    origin.x(12.7);
    std::cout << ' ' << origin.x() << '\n';
}

Это segfaults . Пользуясь чем-то похожим некоторое время назад, мне не повезло.

Вопрос

Как мне сделать this с типом целевого класса доступным в классе фасада?

Мое понимание макета класса

Где-то внутри объекта, в неупорядоченном виде, есть массив и x_getter. Используя reinterpret_cast, я пытаюсь обмануть его, думая, что this - это coordinates, но когда он выполняет operator[], используемое смещение немного смещается, что выходит за пределы объекта, Таким образом, segfaulting.

1 Ответ

0 голосов
/ 30 апреля 2018

Проблема здесь в том, что reinterpret_cast не работает, потому что указатель this не указывает на начало класса coordinates, поскольку он наследуется от array до наследования от x_getter. Расположение памяти для этого класса выглядит следующим образом:

coordinates
|- std::array<double, 3>
|- x_getter

Когда вы используете reinterpret_cast<T*>(this) адрес, сохраненный в указателе this, является адресом объекта x_getter, но вы заставляете компилятор предполагать, что это на самом деле адрес объекта coordinates. Поэтому разыменование такого указателя на производный класс приводит ко всем видам неопределенного поведения.

Обычно CRTP должен использовать static_cast внутри метода:

double x() const
{
    return (*static_cast<TDerived const *>(this))[0];
}

В отличие от reinterpret_cast, static_cast правильно настроит указатель this, чтобы правильно указывать на производный объект.

...