结合C++11的新特性来解析C++中的枚举与联合

2020-01-06 14:21:27刘景俊

请注意,可能会很容易误用前面示例中的 Input 结构。 完全由用户负责正确使用鉴别器来访问保存数据的成员。 你可以通过使联合成为专用并提供特殊访问函数(如下一个示例所示)来防止误用。
无限制的联合 (C++11)
在 C++03 及更低版本中,联合可以包含具有类类型的非静态数据成员,只要该类型没有用户提供的构造函数、析构函数或赋值运算符即可。 在 C++11 中,消除了这些限制。 如果在联合中包含这样一个成员,则编译器会自动将不是用户提供的任何特殊成员函数标记为已删除。 如果联合是类或结构中的匿名联合,则类或结构的不是用户提供的任何特殊成员函数都会标记为已删除。 下面的示例演示如何处理联合的某个成员具有需要此特殊处理的成员的情况:


// for MyVariant
#include <crtdbg.h>
#include <new>
#include <utility>

// for sample objects and output
#include <string>
#include <vector>
#include <iostream>

using namespace std;

struct A 
{
  A() = default;
  A(int i, const string& str) : num(i), name(str) {}
  
  int num;
  string name;
  //...
};

struct B 
{
  B() = default;
  B(int i, const string& str) : num(i), name(str) {}
  
  int num;
  string name;
  vector<int> vec;
  // ...
};

enum class Kind { None, A, B, Integer };

#pragma warning (push)
#pragma warning(disable:4624)
class MyVariant
{
public:
  MyVariant()
    : kind_(Kind::None)
  {
  }

  MyVariant(Kind kind)
    : kind_(kind)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A();
      break;
    case Kind::B:
      new (&b_) B();
      break;
    case Kind::Integer:
      i_ = 0;
      break;
    default:
      _ASSERT(false);
      break;
    }
  }

  ~MyVariant()
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      a_.~A();
      break;
    case Kind::B:
      b_.~B();
      break;
    case Kind::Integer:
      break;
    default:
      _ASSERT(false);
      break;
    }
    kind_ = Kind::None;
  }

  MyVariant(const MyVariant& other)
    : kind_(other.kind_)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A(other.a_);
      break;
    case Kind::B:
      new (&b_) B(other.b_);
      break;
    case Kind::Integer:
      i_ = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
  }

  MyVariant(MyVariant&& other)
    : kind_(other.kind_)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A(move(other.a_));
      break;
    case Kind::B:
      new (&b_) B(move(other.b_));
      break;
    case Kind::Integer:
      i_ = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
    other.kind_ = Kind::None;
  }

  MyVariant& operator=(const MyVariant& other)
  {
    if (&other != this)
    {
      switch (other.kind_)
      {
      case Kind::None:
        this->~MyVariant();
        break;
      case Kind::A:
        *this = other.a_;
        break;
      case Kind::B:
        *this = other.b_;
        break;
      case Kind::Integer:
        *this = other.i_;
        break;
      default:
        _ASSERT(false);
        break;
      }
    }
    return *this;
  }

  MyVariant& operator=(MyVariant&& other)
  {
    _ASSERT(this != &other);
    switch (other.kind_)
    {
    case Kind::None:
      this->~MyVariant();
      break;
    case Kind::A:
      *this = move(other.a_);
      break;
    case Kind::B:
      *this = move(other.b_);
      break;
    case Kind::Integer:
      *this = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
    other.kind_ = Kind::None;
    return *this;
  }

  MyVariant(const A& a)
    : kind_(Kind::A), a_(a)
  {
  }

  MyVariant(A&& a)
    : kind_(Kind::A), a_(move(a))
  {
  }

  MyVariant& operator=(const A& a)
  {
    if (kind_ != Kind::A)
    {
      this->~MyVariant();
      new (this) MyVariant(a);
    }
    else
    {
      a_ = a;
    }
    return *this;
  }

  MyVariant& operator=(A&& a)
  {
    if (kind_ != Kind::A)
    {
      this->~MyVariant();
      new (this) MyVariant(move(a));
    }
    else
    {
      a_ = move(a);
    }
    return *this;
  }

  MyVariant(const B& b)
    : kind_(Kind::B), b_(b)
  {
  }

  MyVariant(B&& b)
    : kind_(Kind::B), b_(move(b))
  {
  }

  MyVariant& operator=(const B& b)
  {
    if (kind_ != Kind::B)
    {
      this->~MyVariant();
      new (this) MyVariant(b);
    }
    else
    {
      b_ = b;
    }
    return *this;
  }

  MyVariant& operator=(B&& b)
  {
    if (kind_ != Kind::B)
    {
      this->~MyVariant();
      new (this) MyVariant(move(b));
    }
    else
    {
      b_ = move(b);
    }
    return *this;
  }

  MyVariant(int i)
    : kind_(Kind::Integer), i_(i)
  {
  }

  MyVariant& operator=(int i)
  {
    if (kind_ != Kind::Integer)
    {
      this->~MyVariant();
      new (this) MyVariant(i);
    }
    else
    {
      i_ = i;
    }
    return *this;
  }

  Kind GetKind() const
  {
    return kind_;
  }

  A& GetA()
  {
    _ASSERT(kind_ == Kind::A);
    return a_;
  }

  const A& GetA() const
  {
    _ASSERT(kind_ == Kind::A);
    return a_;
  }

  B& GetB()
  {
    _ASSERT(kind_ == Kind::B);
    return b_;
  }

  const B& GetB() const
  {
    _ASSERT(kind_ == Kind::B);
    return b_;
  }

  int& GetInteger()
  {
    _ASSERT(kind_ == Kind::Integer);
    return i_;
  }

  const int& GetInteger() const
  {
    _ASSERT(kind_ == Kind::Integer);
    return i_;
  }

private:
  Kind kind_;
  union
  {
    A a_;
    B b_;
    int i_;
  };
};
#pragma warning (pop)

int main()
{
  A a(1, "Hello from A");
  B b(2, "Hello from B");

  MyVariant mv_1 = a;

  cout << "mv_1 = a: " << mv_1.GetA().name << endl;
  mv_1 = b;
  cout << "mv_1 = b: " << mv_1.GetB().name << endl;
  mv_1 = A(3, "hello again from A");
  cout << R"aaa(mv_1 = A(3, "hello again from A"): )aaa" << mv_1.GetA().name << endl;
  mv_1 = 42;
  cout << "mv_1 = 42: " << mv_1.GetInteger() << endl;

  b.vec = { 10,20,30,40,50 };

  mv_1 = move(b);
  cout << "After move, mv_1 = b: vec.size = " << mv_1.GetB().vec.size() << endl;

  cout << endl << "Press a letter" << endl;
  char c;
  cin >> c;
}
#include <queue>
#include <iostream>
using namespace std;


enum class WeatherDataType
{
  Temperature, Wind
};

struct TempData
{
  TempData() : StationId(""), time(0), current(0), maxTemp(0), minTemp(0) {}
  TempData(string id, time_t t, double cur, double max, double min)
    : StationId(id), time(t), current(cur), maxTemp(max), minTemp(0) {}
  string StationId;
  time_t time = 0;
  double current;
  double maxTemp;
  double minTemp;
};

struct WindData
{
  int StationId;
  time_t time;
  int speed;
  short direction;
};

struct Input
{
  Input() {}
  Input(const Input&) {}

  ~Input()
  {
    if (type == WeatherDataType::Temperature)
    {
      temp.StationId.~string();
    }
  }

  WeatherDataType type;
  void SetTemp(const TempData& td)
  {
    type = WeatherDataType::Temperature;

    // must use placement new because of string member!
    new(&temp) TempData(td);
  }

  TempData GetTemp()
  {
    if (type == WeatherDataType::Temperature)
      return temp;
    else
      throw logic_error("Can't return TempData when Input holds a WindData");
  }
  void SetWind(WindData wd)
  {
    // Explicitly delete struct member that has a 
    // non-trivial constructor
    if (type == WeatherDataType::Temperature)
    {
      temp.StationId.~string();
    }
    wind = wd; //placement new not required.
  }
  WindData GetWind()
  {
    if (type == WeatherDataType::Wind)
    {
      return wind;
    }
    else
      throw logic_error("Can't return WindData when Input holds a TempData");
  }

private:

  union
  {
    TempData temp;
    WindData wind;
  };
};