po utworzeniu unii z klasy, która nie jest w zasadzie zwykły stare dane, w C++ 11 to pozwala. Ale idzie i niejawnie usuwa większość specjalnych funkcji składowych, takich jak destruktor.
union my_union
{
string str;
int a;
};
praktycznym problemem jest to, że w momencie zniszczenia C++ nie wiem który powyższych częściach Unii są ważne.
Możesz obejść ten problem, używając oznaczonego powiązania i śledzenia aktywnego, i ręcznie robiąc zniszczenie w tym przypadku.
Więc możemy dostać coś takiego:
struct tagged_union {
enum active {nothing, string, integer} which_active;
template<active...As>
using actives = std::integral_sequence<active, As...>
using my_actives = actives<nothing, string, integer>;
struct nothingness {};
union my_union
{
nothingness nothing;
std::string str;
int a;
~my_union() {};
} data;
using my_tuple = std::tuple<nothingness, std::string, int>;
template<active which>
using get_type = std::tuple_element_t<(std::size_t)which, my_tuple>;
template<class F>
void operate_on(F&& f) {
operate_on_internal(my_actives{}, std::forward<F>(f));
}
template<class T, class F>
decltype(auto) operate_on_by_type(F&& f) {
return std::forward<F>(f)(reinterpret_cast<T*>(&data));
}
// const versions go here
private:
// a small magic switch:
template<active...As, class F>
void operate_on_internal(actives<As...>, F&& f) {
using ptr = void(*)(my_union*,std::decay_t<F>*);
const ptr table[]={
[](my_union* self, std::decay_t<F>* pf){
std::forward<F>(*pf)(*(get_type<As>*)self);
}...,
nullptr
};
table[which](&data, std::address_of(f));
}
public:
template<class...Args>
tagged_union(Active w, Args&&...args) {
operate_on([&](auto& t){
using T = std::decay_t<decltype(t)>();
::new((void*)std::addressof(t)) T(std::forward<Args>(args)...);
which = w;
});
}
tagged_union():tagged_union(nothing){}
~tagged_union() {
operate_on([](auto& t){
using T = std::decay_t<decltype(t)>();
t->~T();
which=nothing;
::new((void*)std::addressof(t)) nothingness{}; // "leaks" but we don't care
});
}
};
, która jest w zasadzie prymitywny szkic tego, jak coś działa, jeśli boost::variant
napisany w C++ 11.
Zawiera trochę ciężkiego mojo.
Powyższe nie zostało skompilowane, ale projekt jest dźwiękowy. Niektóre kompilatory C++ 14 nominalnie nie lubią robić paczek rozszerzać się wokół pełnej lambdy, która wymagałaby jeszcze większej ilości kart.
Ponownie otwarte: C++ 11 znacznie rozszerzyło to, co jest dozwolone w związku, a między innymi "std :: string" jest teraz dozwolone. –
@PeteBecker Dostaję to, ale czy nie jest to opisane w [this] (http://stackoverflow.com/a/3521998/4342498) odpowiedzi od Q? – NathanOliver
W jaki sposób planujesz "wiedzieć", który członek związku będzie czytał później z wektora? –