C ++:包含它所属的类的联合

时间:2016-12-21 15:27:54

标签: c++ c++11 clang unions

虽然这段代码编译没有任何错误,但我怀疑它会按预期工作。允许这样的嵌套吗?我不能使用boost和c ++ 17。

class Node;

typedef struct Value {      
    ValueTag type;

    union {         
        std::int32_t                          integerValue;
        std::float_t                          floatValue;
        bool                                  boolValue;
        std::vector<Node>                     arrayValue;
        std::unordered_map<std::string, Node> dictionaryValue;
    };          
} Value;

class Node {                
private:        
    Value m_value;          
public:                 
    virtual ~Node();    
};

2 个答案:

答案 0 :(得分:5)

  

我怀疑它会按预期工作。

你是对的。它不会。首先,如果您尝试构建Node,它实际上不会编译。你会得到类似的东西:

prog.cc:26:10: error: call to implicitly-deleted default constructor of 'Node'
    Node n;
         ^

那是因为在union中,隐式删除了任何非平凡的操作。您必须定义~Value()来做正确的事情。其中,假设type是我们实际所在的联合中的哪个元素的索引,要打开它并调用适当的析构函数。然后为复制和移动做同样的事情。

也就是说,不完整类型的嵌套也不行。只要在第一次使用之前完成,vector就可以使用不完整的类型。但是unordered_map没有这个津贴。您必须将值类型包装在其他内容中 - 例如unique_ptr<Node>shared_ptr<Node>

Value中的内容是一种具有百万个不同名称的常见模式:有区别的联合,总和类型,或者最常见的是variant。我建议您不要重新发明轮子,而是使用std::variant(如果您使用的是最近的编译器来支持这样的事情)或boost::variant(否则)。有了这个,你有:

using Value = variant<int32_t, float, bool,
    std::vector<Node>, std::unordered_map<std::string, Node>>;

这种类型已经可以破坏,可复制,移动,并且可以访问。

答案 1 :(得分:-2)

修改 我的原始答案不正确/误导,请忽略它。因为看到下面的评论。无论如何,考虑std :: variant或boost :: variant:)

非PlainOldData(POD)在c-union中不安全。

特别是对于这个问题,std :: variant进入了c ++ 17标准。您可以将boost :: variant用于早期版本的C ++。

问题是,union对构造函数和析构函数一无所知。因此,不可能使用union的元素做更多一般事情,这是必要的(例如动态内存分配)