如何使用两种类型的并集

时间:2016-05-04 12:15:04

标签: c++ unions

我正在努力制作一个可以容纳vectorstring的{​​{1}}。

我已经尝试过以下代码,但是我收到了编译错误

  

错误:使用已删除的功能' my_union :: ~my_union()'

我做错了什么?

int

3 个答案:

答案 0 :(得分:14)

来自here

  

如果一个union包含一个带有非平凡特殊成员函数的非静态数据成员(默认构造函数,复制/移动构造函数,复制/移动赋值或析构函数),那么默认情况下该函数将被删除并且需要由程序员明确定义。

您必须为联盟明确定义析构函数,以替换string自动删除的析构函数。

另请注意,这仅在c ++ 11中有效。在早期版本中,根本不能在联合中包含具有非平凡特殊成员函数的类型。

从实际的角度来看,这可能仍然不是一个好主意。

答案 1 :(得分:2)

当您使用基本上不是普通旧数据的类创建联合时,在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进行扩展,但这需要更多的样板。

答案 2 :(得分:1)

在C ++ 11之前,不允许在引用here的联盟中使用std::string

  

Unions不能包含非平凡的非静态数据成员   特殊成员函数(复制构造函数,复制赋值运算符,   或者析构者)。

从C ++ 11开始,你可以在@Rotem已经回答的联合中使用std::string,你需要为字符串明确定义析构函数或者调用析构函数explicitly

str.~basic_string<char>();