实现标记联合的移动构造函数

Implementing a move constructor of a tagged union

本文关键字:移动 构造函数 实现      更新时间:2023-10-16

我使用包含匿名联合和标记的类实现了一个标记联合:

class LogFile
{
  public:
    LogFile(std::ostream& stdStream);
    LogFile(std::ofstream fileStream);
    LogFile(LogFile&& logFile);
    ~LogFile();
    std::ostream& getStream();
  private:
    enum { STD_STREAM, FILE_STREAM } streamType_;
    union
    {
        std::ostream *stdStream_;
        std::ofstream fileStream_;
    };
};

我在实现移动构造函数时遇到问题。在重载的"正常"构造函数中,我知道要初始化哪个联盟成员:

LogFile::LogFile(std::ofstream fileStream)
: streamType_(FILE_STREAM), fileStream_(std::move(fileStream))
{
}

但是在移动构造函数中,我怎么知道我必须初始化哪个stdStream_fileStream_。我无法在初始值设定项列表中检查streamType_的值。

除非您这样做是为了练习,否则请将标记的联合替换为 std::variant 。它更安全。


以后可以使用 placement-new 有条件地调用它,而不是在成员初始值设定项列表中调用构造函数。

通常,如果未在成员初始值设定项列表中指定构造函数,则会调用默认构造函数。但是对于union {...};的成员,根本没有调用构造函数。

LogFile(LogFile&& other) : streamType_(other.streamType_)
{
    switch (streamType_)
    {
      case FILE_STREAM:
        new (&fileStream_) std::ofstream(std::move(other.fileStream_)); // <--
        break;
      case STD_STREAM:
        stdStream_ = other.stdStream_;
        other.stdStream_ = 0;
        break;
    }
}

请注意,您必须在 ~LogFile() 中手动调用析构函数,并且还需要自定义移动赋值。这就是为什么std::variant更好。