如何使用两种类型的联合
How to use a union of two types
我正在尝试制作一个可以容纳 string
和 int
的 vector
。
我试过下面的代码,但出现编译错误
error: use of deleted function 'my_union::~my_union()'
我做错了什么?
#include <iostream>
#include <vector>
using namespace std;
union my_union
{
string str;
int a;
};
int main()
{
vector<my_union> v;
my_union u; // error: use of deleted function 'my_union::~my_union()'
u.str = "foo";
v.push_back(u);
return 0;
}
来自here
If a union contains a non-static data member with a non-trivial special member function (default constructor, copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.
您必须为您的联合显式定义一个析构函数,以替换为 string
自动删除的析构函数。
另请注意,这仅在 c++11 中有效。在早期版本中,您根本不能在联合内拥有具有非平凡特殊成员函数的类型。
从实用的角度来看,这可能仍然不是一个好主意。
在 C++11 之前,不允许在引用 here:
的联合中使用 std::string
Unions cannot contain a non-static data member with a non-trivial
special member function (copy constructor, copy-assignment operator,
or destructor).
从 C++11 开始,您可以在联合中使用 std::string
,正如 @Rotem, you need to define a destructor explicitly for string or call the destructor explicitly
已经回答的那样
str.~basic_string<char>();
当您使用基本上不是普通旧数据的 class 创建联合时,在 C++11 中它允许您。但它会隐式删除大部分特殊成员函数,如析构函数。
union my_union
{
string str;
int a;
};
实际问题是,在销毁点,C++ 不知道上述联合部分中的哪个 是有效的。
您可以通过使用标记联合来解决此问题,并跟踪哪个处于活动状态,并在这种情况下手动进行销毁。
所以我们可以得到类似的东西:
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
});
}
};
这基本上是用 C++11 编写的类似 boost::variant
的工作原理的原始草图。
它涉及一些沉重的魔力。
以上未编译,但设计合理。一些名义上的 C++14 编译器不喜欢围绕完整的 lambda 进行包扩展,然而,这将需要更多的样板文件。
我正在尝试制作一个可以容纳 string
和 int
的 vector
。
我试过下面的代码,但出现编译错误
error: use of deleted function 'my_union::~my_union()'
我做错了什么?
#include <iostream>
#include <vector>
using namespace std;
union my_union
{
string str;
int a;
};
int main()
{
vector<my_union> v;
my_union u; // error: use of deleted function 'my_union::~my_union()'
u.str = "foo";
v.push_back(u);
return 0;
}
来自here
If a union contains a non-static data member with a non-trivial special member function (default constructor, copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.
您必须为您的联合显式定义一个析构函数,以替换为 string
自动删除的析构函数。
另请注意,这仅在 c++11 中有效。在早期版本中,您根本不能在联合内拥有具有非平凡特殊成员函数的类型。
从实用的角度来看,这可能仍然不是一个好主意。
在 C++11 之前,不允许在引用 here:
的联合中使用std::string
Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).
从 C++11 开始,您可以在联合中使用 std::string
,正如 @Rotem, you need to define a destructor explicitly for string or call the destructor explicitly
str.~basic_string<char>();
当您使用基本上不是普通旧数据的 class 创建联合时,在 C++11 中它允许您。但它会隐式删除大部分特殊成员函数,如析构函数。
union my_union
{
string str;
int a;
};
实际问题是,在销毁点,C++ 不知道上述联合部分中的哪个 是有效的。
您可以通过使用标记联合来解决此问题,并跟踪哪个处于活动状态,并在这种情况下手动进行销毁。
所以我们可以得到类似的东西:
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
});
}
};
这基本上是用 C++11 编写的类似 boost::variant
的工作原理的原始草图。
它涉及一些沉重的魔力。
以上未编译,但设计合理。一些名义上的 C++14 编译器不喜欢围绕完整的 lambda 进行包扩展,然而,这将需要更多的样板文件。