rufen Wenn Sie eine Vereinigung mit einer Klasse, die im Grunde nicht nur alte Daten, in C++ ist 11 es Ihnen ermöglicht. Aber es geht und implizit löscht die meisten der speziellen Member-Funktionen wie der Destruktor.
union my_union
{
string str;
int a;
};
das praktische Problem ist an der Stelle der Zerstörung C++, dass nicht kennt die der oben genannten Teile der Union gültig sind.
Sie können dies umgehen, indem Sie eine markierte Union verwenden und die aktive Spur beibehalten und in diesem Fall manuell die Zerstörung vornehmen.
So können wir so etwas wie erhalten:
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
});
}
};
, die im Grunde ist eine primitive Skizze, wie so etwas wie boost::variant
funktioniert, wenn ++ 11 C geschrieben.
Es beinhaltet einige schwere mojo.
Das obige wurde nicht kompiliert, aber das Design ist solide. Einige nominal C++ 14-Compiler mögen es nicht, wenn ein Pack um ein volles Lambda erweitert wird, was jedoch noch mehr Standard erfordert.
Erneut geöffnet: C++ 11 erweitert erheblich, was in einer Union erlaubt ist, und 'std :: string', unter anderem, ist jetzt erlaubt. –
@PeteBecker Ich bekomme das aber ist das nicht in [this] abgedeckt (http://stackoverflow.com/a/3521998/4342498) Antwort von der Q? – NathanOliver
Wie planen Sie, zu wissen, welches Mitglied der Gewerkschaft später aus dem Vektor lesen soll? –