Использование std :: vector, boost :: option и типов вместе со ссылочными полями

0

У меня есть следующие два класса:

struct A {

    A() : state(0) { }
    A(int state_arg) 
        : state{ state_arg } { }

    int state;
};

struct B {

    B(int state_arg, const int& ref)
        : state{ state_arg }, ref{ ref } { }

    int state;
    const int& ref;
};

Я притворяюсь, что поле ref во втором является ссылкой на целое число в другом месте, возможно (но не обязательно) state поля некоторого экземпляра типа B

Теперь я хочу выполнять некоторые операции над этими типами. Действительно, я использую библиотеку boost :: variant.

using my_type = boost::variant<A, B>;

Теперь, когда я работаю с переменными my_type все работает так, как ожидалось. Например:

int main() {

    my_type a(A(45));
    my_type b(B(45, boost::get<A>(a).state));

    A& at = boost::get<A>(a);
    B& bt = boost::get<B>(b);

    if (at.state == bt.ref) {
        std::cout << "AS EXPECTED" << std::endl;
    }
    // that prints "AS EXPECTED"
}

Но когда я работаю с std::vector my_type все идет не так!

int main() {

    std::vector<my_type> vec;
    vec.push_back(A(45));
    vec.push_back(B(45, boost::get<A>(vec[0]).state));

    A& at = boost::get<A>(vec[0]);
    B& bt = boost::get<B>(vec[1]);

    if (at.state == bt.ref) {
        std::cout << "SHOULD I EXPECTED THIS ?" << std::endl;
    }
    // the code doesn't print
}

Теперь я хочу знать, что здесь происходит, т.е. Что происходит в том, что в вышеприведенном коде оценка if дает false?

И, возможно, я хотел бы получить некоторые советы о том, как выполнить эти задачи. Заранее спасибо.

  • 1
    Второй push_back вызывает перераспределение, делающее недействительными все существующие ссылки на элементы вектора, включая ту, которую вы передали конструктору B
Теги:
boost
stdvector
std
boost-variant

1 ответ

1
Лучший ответ

Проблема заключается в том, что когда вы добавляете второй элемент в вектор, он перераспределяет больше памяти и перемещает первый объект в новое место, и вы обманываете ссылку. Простым решением было бы зарезервировать достаточно памяти в std::vector заранее, чтобы предотвратить перераспределение или использовать другой контейнер, который не перемещает объекты. Но у вашего оригинального решения есть недостаток дизайна - он полагается на тот факт, что объект, на который он ссылается, должен пережить его. Но ваша логика не может гарантировать, что это приведет к проблеме, которую вы видите в std::vector. Тот же вопрос может быть в первом примере, если объект b переживет объект каким - то образом. a Лучшим решением было бы использовать интеллектуальные указатели и позволить объектам типа B иметь общий или слабый указатель на объект типа A, зависит от того, какое владение вы хотите иметь. Таким образом, у вас будут общие указатели в std::vector и перераспределение памяти, это не повлияет на вас:

struct A {

    A() : state(0) { }
    A(int state_arg) 
        : state{ state_arg } { }

    int state;
};
typedef std::shared_ptr<A> APtr;

struct B {

    B(int state_arg, const APtr& ptr)
        : state{ state_arg }, aptr{ ptr } { }

    int state;
    APtr aptr;
    int ref() const { return aptr->state; }
}
typedef std::shared_ptr<B> BPtr;

using my_type = boost::variant<APtr, BPtr>;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню