【问题标题】:Implementing a move constructor of a tagged union实现标记联合的移动构造函数
【发布时间】:2018-05-01 10:44:13
【问题描述】:

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

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::ostream 指针或引用中以避免联合。
  • 如果您的目的是学习如何在这种棘手的情况下进行正确的移动构造,那么这是一回事。但是,如果您的真正目的是让这个工作正常进行,请将这个 union 替换为 std::variant 并让它为您完成所有的思考。
  • std::variant 对我来说太新了,我的编译器还不支持。在我的特殊情况下,我最终使用了@Rakete1111 建议的std::unique_ptr<std::ostream>。对于stdout,您需要在堆上创建一个std::ostream 的新实例,其底层缓冲区为std::coutstd::unique_ptr<std::ostream>(new std::ostream(std::cout.rdbuf()))(否则std::unique_ptr 将尝试删除静态分配的std::cout

标签: c++ c++11 unions move-constructor


【解决方案1】:

除非您这样做是为了练习,否则请用 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 更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-03
    • 1970-01-01
    • 2019-05-17
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多