实现标记联合的移动构造函数
Implementing a move constructor of a tagged union
我使用包含匿名联合和标记的 class 实现了标记联合:
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_;
};
};
我在实现移动构造函数时遇到问题。在重载的 "normal" 构造函数中,我知道要初始化哪个联合成员:
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
更好的原因。
我使用包含匿名联合和标记的 class 实现了标记联合:
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_;
};
};
我在实现移动构造函数时遇到问题。在重载的 "normal" 构造函数中,我知道要初始化哪个联合成员:
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
更好的原因。