C ++ 11 дает нам возможность создавать анонимные союзы с нетривиальными членами.Иногда это может быть очень полезно - например, если я хочу создать класс Holder для какого-то нетривиального объекта без ctor по умолчанию.
Давайте сделаем этот нетривиальный объект более интересным, предоставив ему виртуальный метод:
#include <stdint.h>
#include <stdio.h>
struct Base
{
virtual void something() { printf("something\n"); }
};
struct NonTrivial : Base
{
explicit NonTrivial( int ) : a(1), b(2), c(3), d(4) { printf("NonTrivial\n"); }
virtual void something() override { printf("something non trivial\n"); }
int a;
int b;
int c;
int d;
};
struct Holder
{
Holder() : isNonTrivial(false), dummy(0x77) {}
Holder( NonTrivial n) : isNonTrivial(true), nonTrivial( n ) {}
bool isNonTrivial;
union
{
int dummy;
NonTrivial nonTrivial;
};
Holder & operator=( const Holder & rhs )
{
isNonTrivial = rhs.isNonTrivial;
if( isNonTrivial )
nonTrivial = rhs.nonTrivial;
return *this;
}
};
int main() {
Holder holder_1;
NonTrivial n(1);
Holder holder_2( n );
holder_1 = holder_2;
holder_2.nonTrivial.something();
holder_1.nonTrivial.something();
return 0;
}
Это просто работает.Однако, это работает только , потому что компилятор фактически не делает здесь виртуальный вызов.Давайте сделаем это принудительно:
Base * ptr = &holder_1.nonTrivial;
ptr->something();
Это приводит к segfault.
Но почему?Я сделал более или менее очевидную вещь - проверил, содержит ли держатель нетривиальный объект, и если так - скопировал его.
После прочтения сборки я увидел, что этот operator=
на самом деле не копирует указатель vtable из rhs.nonTrivial.,Я предполагаю, что это происходит потому, что operator=
для NonTrivial следует вызывать только для полностью построенного объекта, а полностью построенный объект должен уже иметь инициализированный указатель vtable - так зачем беспокоиться и копировать его?
Вопросы:
- Правильно ли мое мышление?
- Как должна выглядеть
operator=
для создания полной копии нетривиального объекта?У меня есть две идеи - полностью удалить operator=
и заставить пользователя использовать копию ctor - или использовать новое размещение вместо nonTrivial = rhs.nonTrivial
- но, возможно, есть другие варианты?
PS Язнаю о std :: необязательный и тому подобное, я пытаюсь понять, как это сделать сам.