[关闭]
@RR 2017-03-22T03:55:30.000000Z 字数 1467 阅读 139

在union中包含一个std::string类型

flex-c++


为什么不能再union里面包含一个std::string类型。

  1. union SimpleUnion {
  2. int i;
  3. bool b;
  4. std::string s;
  5. };
  6. SimpleUnion u;

the default constructor of "SimpleUnion" cannot be referenced -- it is a deleted function Args

上面这段代码会有一个编译错误。原因在于std::string是一个class对象,它有自己的构造和析构函数,不允许直接这么用在union里面。考虑下面这样的代码, C++的编译器在构造SimpleUnion u;的时候,没法预知union里面需要的是一个std::string,而调用其构造函数。

  1. SimpleUnion u;
  2. u.s.size();

什么样的对象能简单的放在union里面?

只有is trivially default constructible的对象才能这样简单的放在union里面。比如一个类包含一个非默认的构造函数;包含虚函数之类的,那么它就不是trivially default constructible。比如说下面的C和C2结构体都不满足条件。

  1. struct C{
  2. void virtual fun(){}
  3. };
  4. struct C2 {
  5. C2(){}
  6. };
  7. static_assert(std::is_trivially_default_constructible<C2>::value,"C is not trivial default constructible");

对应的在static_assert这里都会有编译错误。这里的std::is_trivially_default_constructible是一个C++的标准模板,可以用来批判一个对象是不是trivially default constructible

怎么样把一个std::string放到union里面呢

既然编译器无法自动构造union对象,那么给union加上构造函数就可以了。比如下面的代码不会有编译错误。

  1. union SimpleUnion {
  2. SimpleUnion() : s("") {}
  3. ~SimpleUnion() {}
  4. int i;
  5. bool b;
  6. std::string s;
  7. };
  8. SimpleUnion u;

但是这仅仅是没有编译错误了,但是实际上还是有很多问题。比如说std::string s的析构函数怎么被调用?在SimpleUnion的析构函数里面,我们无法判断当前union里面是一个int还是个std::string。所以说,一般来说你只能放一个对象的指针在union里面。而在union之外来管理指针的生存周期。

如何完美的解决这个问题?使用variant模板

std::variant 是一个类型安全的union,是在C++14里面添加的新特性,但是暂时在主流的C++编译器里面并没有支持。所以我使用了boost库里面的variant,他们是类似的。

  1. int main(int argc, char** argv)
  2. {
  3. using Variant = boost::variant<int, std::string>;
  4. Variant v;
  5. v = 1;
  6. v = "string";
  7. std::string s = boost::get<std::string>(v);
  8. return 0;
  9. }

variant,tuple, vector概念上的区别。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注