У меня есть следующие два класса:
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?
И, возможно, я хотел бы получить некоторые советы о том, как выполнить эти задачи. Заранее спасибо.
Проблема заключается в том, что когда вы добавляете второй элемент в вектор, он перераспределяет больше памяти и перемещает первый объект в новое место, и вы обманываете ссылку. Простым решением было бы зарезервировать достаточно памяти в 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>;
push_back
вызывает перераспределение, делающее недействительными все существующие ссылки на элементы вектора, включая ту, которую вы передали конструкторуB