Почему я не могу создать объединение, содержащее объект vec3? - PullRequest
0 голосов
/ 29 мая 2018

Я не могу создать объединение, в котором элемент является или содержит объект glm::vec3 (объект для представления координаты, в данном случае содержащий 3 числа с плавающей запятой). (исходный код для glm :: vec)

Используется в следующем коде:

struct Event {
    enum Type{
        tRaw,
        tAction,
        tCursor,
    } type;
    union {
        SDL_Event raw;
        struct {
            uint16 actionID;
            bool released;
        } action;
        struct {
            glm::vec3 prevPos;
            glm::vec3 pos;
        } cursor; // offending object, compiles if this is removed
    } data;
};

Visual Studio выдает следующую ошибку intellisense.

"Error: the default constructor of "union Event::<unnamed>" cannot be referenced -- it is a deleted function"

Если удалить, объединение компилируется без проблем.Что может быть причиной этой проблемы, и могу ли я что-нибудь сделать, чтобы исправить ее?

1 Ответ

0 голосов
/ 29 мая 2018

Если в вашем union есть нетривиальный тип (то есть тот, для которого язык обеспечивает"правильную" инициализацию, то есть вызов конструктора), вы должны явно написать свои конструкторы / деструкторы:

#include <SDL/SDL.h>
#include <glm/vec3.hpp>
#include <stdint.h>
#include <new>
#include <vector>

struct Event {
    enum Type{
        tRaw,
        tAction,
        tCursor,
    } type;
    struct Cursor {
        glm::vec3 prevPos;
        glm::vec3 pos;
    };
    union {
        SDL_Event raw;
        struct {
            uint16_t actionID;
            bool released;
        } action;
        Cursor cursor;
    };
    Event(const SDL_Event &raw) : type(tRaw) {
        new(&this->raw) SDL_Event(raw);
    }
    Event(uint16_t actionID, bool released) : type(tAction) {
        this->action.actionID = actionID;
        this->action.released = released;
    }
    Event(glm::vec3 prevPos, glm::vec3 pos) : type(tCursor) {
        new(&this->cursor) Cursor{prevPos, pos};
    }
    Event(const Event &rhs) : type(rhs.type) {
        switch(type) {
        case tRaw:      new(&this->raw) SDL_Event(raw); break;
        case tAction:   memcpy((void *)&action, (const void *)&rhs.action, sizeof(action)); break;
        case tCursor:   new(&this->cursor) Cursor(rhs.cursor);
        }
    }

    ~Event() {
        if(type == tCursor) {
            this->cursor.~Cursor();
        }
        // in all other cases, no destructor is needed
    }
};

int main() {
    // Construction
    Event ev(1, false);
    SDL_Event foo;
    Event ev2(foo);
    glm::vec3 pos;
    Event ev3(pos, pos);
    // Copy construction & destruction
    std::vector<Event> events;
    events.push_back(ev);
    events.push_back(ev2);
    events.push_back(ev3);
    events.clear();
    return 0;
}

Некоторые примечания:

  • Я избегал члена data, выбрав вместо этого анонимного union;это позволяет избежать большого количества шаблонов, так как в противном случае я должен был бы написать эти конструкторы внутри union (потому что это union конструктор, который удален и должен быть явно определен), а затем добавитьэкспедиторы снаружи;это также значительно упрощает написание деструктора (опять же, это должно было бы быть написано внутри union, но union не знает внешнего type; вы можете обойти это, но это утомительно и многословно);
  • Мне пришлось явно назвать объединение Cursor, в противном случае синтаксически невозможно вызвать его деструктор (исключая уловки шаблонов);
  • Я не реализовалоператор присваивания;это не сложно, но, честно говоря, это довольно утомительно.Вы можете найти базовый проект (проверьте, если то же самое type; если то же самое, выполните обычное задание; в противном случае уничтожьте активного члена и поместите new на нового) в ссылке, которую я разместил ранее в комментариях.

Все это, как говорится, уже реализовано в C ++ 17 в более общем виде, как std::variant, так что если у вас естьдостаточно недавний компилятор, вы можете использовать его вместо этого.

...