Дан союз:
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
#include <cassert>
#include <cstdlib>
struct A { int a; };
struct B { int b; };
template< typename X >
struct S
{
std::size_t tag;
std::unique_ptr< X > x;
};
union U
{
S< A > a;
S< B > b;
U(A x) : a{0, std::make_unique< A >(x)} { ; }
U(B x) : b{1, std::make_unique< B >(x)} { ; }
std::size_t tag() { return a.tag; }
~U()
{
switch (tag()) {
case 0 : {
a.~S< A >();
break;
}
case 1 : {
b.~S< B >();
break;
}
default : assert(false);
}
}
void
swap(U & u) noexcept
{
a.x.swap(u.a.x);
std::swap(a.tag, u.a.tag);
}
};
static_assert(std::is_standard_layout< U >{});
int
main()
{
U a{A{ 0}};
U b{B{~0}};
assert((a.tag() == 0) && (a.a.x->a == 0));
assert((b.tag() == 1) && (b.b.x->b == ~0));
a.swap(b);
assert((a.tag() == 1) && (a.b.x->b == ~0));
assert((b.tag() == 0) && (b.a.x->a == 0));
return EXIT_SUCCESS;
}
Функция U::tag()
верна, поскольку позволяет проверять общую начальную подпоследовательность альтернативных элементов данных в U
подобных объединениях.
U::swap()
работает, но законно ли это для std::unique_ptr
s? Разрешено ли обменивать неактивные std::unique_ptr
s альтернативные элементы данных U
подобных объединений?
Это кажется допустимым из-за простой природы std::unique_ptr< X >
: это просто оболочка над X *
и для любых A
и B
я уверен, что static_assert((sizeof(A *) == sizeof(B *)) && (alignof(A *) == alignof(B *)));
выполняется, а расположение указателей идентично для всех типов (кроме указателей на данные-члены и функции-члены классов). Это правда?
Пример кода работает нормально. Но скорее всего есть УБ, если мы читаем стандарт.
std::unique_ptr
имеет стандартный макет? Не могли бы вы добавить статическое утверждение? - person Kerrek SB   schedule 23.09.2015tag()
. - person RamblingMad   schedule 23.09.2015