包含与非平凡成员

时间:2015-05-27 20:53:46

标签: c++ c++11 constructor c++14 unions

我正在尝试实现一个自定义变体类型,它使用union来存储各种不同类型的数据。在字段type_id中,我计划存储联合中存储的数据的类型。工会包含非平凡的成员。这是我目前的实施:

struct MyVariant {
  enum { t_invalid, t_string, t_int, t_double, t_ptr, t_dictionary } type_id;
  union {
    int                             as_int;
    double                          as_double;
    std::string                     as_string;
    std::unique_ptr<int>            as_ptr;
    std::map<int, double>           as_dictionary;
  };
};

我尝试创建MyVariant的实例,如下所示:

MyVariant v;

我收到错误消息:调用隐式删除的MyVariant默认构造函数。所以,我尝试手动实现构造函数,如下所示:

MyVariant() : type_id{t_int}, as_int{0} {}

这给我一个类似的错误消息:尝试使用已删除的功能。 接下来,我尝试实现以下构造函数:

MyVariant(int value) : type_id{t_int}, as_int{value} {}

并构建我的实例,如下所示:

MyVariant v{123};

=&GT;相同的错误消息:尝试使用已删除的功能

我也开始实现一个拷贝构造函数,如下所示。但是,当然这对编译器错误没有帮助。

MyVariant::MyVariant(const MyVariant& other)
{
    type_id = other.type_id;
    switch (type_id) {
        case t_invalid:
            break;
        case t_string:
            new (&as_string) std::string();
            as_string = other.as_string;
            break;
        case t_int:
            as_int = other.as_int;
            break;
        case t_double:
            as_double = other.as_double;
            break;
        case t_ptr:
            new (&as_ptr) std::unique_ptr<int>(nullptr);
            as_ptr = std::make_unique<int>(*other.as_ptr);
            break;
        case t_dictionary:
            new (&as_dictionary) std::map<int, double>();
            // TODO: copy values from other
            break;
    }
}

我使用Xcode和Apple LLVM 6.1作为编译器。

主要问题是:为什么我会得到我遇到的编译器错误以及如何修改我的代码以使其编译?

另外一个问题是:我是否正确地使用构造函数和复制构造函数的实现?

1 个答案:

答案 0 :(得分:8)

您的联盟拥有stringunique_ptrmap类型的数据成员,所有这些成员都有非平凡的默认/复制/移动构造函数,复制/移动赋值运算符和析构函数。因此,所有这些都被隐含地删除了你的工会。

§9.5/ 2 [class.union]

  

... [注意:如果有任何非静态数据成员   union的一个非平凡的默认构造函数(12.1),复制构造函数(12.8),移动构造函数(12.8),复制赋值运算符(12.8),移动赋值运算符(12.8)或析构函数(12.4),对应的   union的成员函数必须是用户提供的,否则将隐式删除(8.4.3)union。 -end note ]

所以你必须为你的工会手动实现这些。至少,为了能够创建MyVariant的实例,该类需要是可构造和可破坏的。所以你需要

MyVariant() : type_id{t_int}, as_int{0} {}
~MyVariant()
{
  switch(type_id)
  {
      case t_int:
      case t_double:
        // trivially destructible, no need to do anything
        break;
      case t_string:
        as_string.~basic_string();
        break;
      case t_ptr:
        as_ptr.~unique_ptr();
        break;
      case t_dictionary:
        as_dictionary.~map();
        break;
      case t_invalid:
        // do nothing
        break;
      default:
        throw std::runtime_error("unknown type");
  }
}

你的拷贝构造函数实现看起来是有效的,但我做的不同的是,首先默认构造成员,然后从源对象复制,只需复制构建新的调用本身。

MyVariant(const MyVariant& other)
{
  type_id = other.type_id;
  switch (type_id) {
      case t_invalid:
          break;
      case t_string:
          new (&as_string) auto(other.as_string);
          break;
      case t_int:
          as_int = other.as_int;
          break;
      case t_double:
          as_double = other.as_double;
          break;
      case t_ptr:
          new (&as_ptr) auto(std::make_unique<int>(*other.as_ptr));
          break;
      case t_dictionary:
          new (&as_dictionary) auto(other.as_dictionary);
          break;
  }

Live demo

请注意,如果unique_ptr成员处于活动状态,并且通过基类指针存储指向某个派生类实例的指针,那么复制构造函数实现将只复制基类部分。

最后,除非您将此作为学习练习,否则我强烈建议您使用Boost.Variant而不是自己动手。