2014-11-12 1 views
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 
} 

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

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

+1

Второй 'push_back' приводит к перераспределению, недействительности все существующие ссылки на элементы вектора, в том числе один, который вы передали' B'. –

ответ

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>; 
Смежные вопросы