包含与非平凡成员的联合的类的构造函数和复制构造函数

j00*_*0hi 11 c++ constructor unions c++11 c++14

我正在尝试实现一个自定义变体类型,它使用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;
  };
};
Run Code Online (Sandbox Code Playgroud)

我尝试创建一个MyVariant类似如下的实例:

MyVariant v;
Run Code Online (Sandbox Code Playgroud)

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

MyVariant() : type_id{t_int}, as_int{0} {}
Run Code Online (Sandbox Code Playgroud)

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

MyVariant(int value) : type_id{t_int}, as_int{value} {}
Run Code Online (Sandbox Code Playgroud)

并构造我的实例如下:

MyVariant v{123};
Run Code Online (Sandbox Code Playgroud)

=>相同的错误消息:尝试使用已删除的功能.

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Pra*_*ian 9

您的联合具有类型的数据成员string,unique_ptr并且map所有这些都具有非平凡的默认/复制/移动构造函数,复制/移动赋值运算符和析构函数.因此,所有这些都被隐含地删除了你的联盟.

§9.5/ 2 [class.union]

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

所以你必须手动为你的工会实现这些.至少,为了能够创建一个实例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");
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
  }
Run Code Online (Sandbox Code Playgroud)

现场演示

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

最后,除非你把它作为一个学习练习,否则我强烈建议你使用Boost.Variant而不是自己动手.

  • Tidbit:`new(&as_dictionary)auto(other.as_dictionary)`等等可以摆脱一些冗余,并且更加重构友好. (3认同)
  • @abraham_hilbert这是[placement new](https://isocpp.org/wiki/faq/dtors#placement-new)语法,您正在传递要在其中构造对象的地址。 (2认同)