包含与非平凡成员的联合的类的构造函数和复制构造函数 [英] Constructor and copy-constructor for class containing union with non-trivial members

查看:500
本文介绍了包含与非平凡成员的联合的类的构造函数和复制构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个自定义变体类型,它使用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}; 

=>同一错误讯息:尝试使用已删除的功能 / p>

我也开始实现一个复制构造函数,它如下所示。然而,当然这对编译器错误没有帮助。

  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:从其他
复制值break;
}
}



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



主要的问题是:为什么我得到编译器错误,我如何修改我的代码来编译?



另外一个问题是:我的构造函数和拷贝构造函数的实现是否正确?

解决方案

您的工会有数据成员 string unique_ptr map ,所有这些都有不重要的默认/复制/移动构造函数,复制/移动赋值运算符和析构函数。



§9.5/ 2 [class.union]




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


至少,为了能够创建 MyVariant 的实例,该类需要是可构造和可破坏的。所以你需要

  MyVariant():type_id {t_int},as_int {0} {} 
〜MyVariant
{
switch(type_id)
{
case t_int:
case t_double:
//简单可破坏,不需要做任何事情
打破;
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:
//不做任何事情
break;
默认值:
throw std :: runtime_error(unknown type);
}
}



您的副本构造函数实现看起来有效, d不同的是,不是首先默认构造成员,然后从源对象复制,只是在放置新调用本身的副本构造。

  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;
}

演示



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



除非你这样做是一个学习练习,我强烈建议你使用 Boost.Variant ,而不是自己滚动。


I am trying to implement a custom variant type which uses a union to store data of various different types. In the field type_id I plan to store which type the data stored in the union is of. The union contains non-trivial members. Here is my current implementation:

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;
  };
};

I try to create an instance of MyVariant like follows:

MyVariant v;

I get the error message: Call to implicitly-deleted default constructor of MyVariant. So, I tried to implement the constructor manually like follows:

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

That gives me a similar error message: Attempt to use a deleted function. Next, I tried to implement the following constructor:

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

and construct my instance like follows:

MyVariant v{123};

=> same error message: Attempt to use a deleted function.

I've also started to implement a copy constructor, it looks like follows. However, of course this doesn't help with the compiler errors.

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;
    }
}

I am using Xcode and Apple LLVM 6.1 as compiler.

The main question is: Why do I get the compiler errors which I'm getting and how do I have to modify my code to make it compile?

The additional question is: Am I on the right way with my implementations for the constructor and copy constructor?

解决方案

Your union has data members of type string, unique_ptr and map, all of which have non-trivial default/copy/move constructors, copy/move assignment operators and destructors. Hence all of these are implicitly deleted for your union.

§9.5/2 [class.union]

... [ Note: If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union. —end note ]

So you must manually implement these for your union. At a minimum, for you to be able to create an instance of MyVariant, the class needs to be constructible and destructible. So you need

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");
  }
}

Your copy constructor implementation looks valid, but what I'd do differently is instead of first default constructing the member, and then copying from the source object, just copy construct in the placement new call itself.

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

Note that if the unique_ptr member is active, and is storing a pointer to some derived class instance via a base class pointer, then your copy constructor implementation will only copy the base class part.

Finally, unless you're doing this as a learning exercise, I'd strongly urge you to use Boost.Variant instead of rolling your own.

这篇关于包含与非平凡成员的联合的类的构造函数和复制构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆