I implemented a tagged union using a class containing an anonymous union and a tag:
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_;
};
};
I have trouble implementing the move constructor. In the overloaded "normal" constructors I know which union member to initialize:
LogFile::LogFile(std::ofstream fileStream)
: streamType_(FILE_STREAM), fileStream_(std::move(fileStream))
{
}
But in the move constructor, how do I know which of stdStream_
or fileStream_
I have to initialize. I can't check the value of streamType_
in the initializer list.
Unless you do it for practice, replace your tagged union with a std::variant
. It's a lot more safe.
Instead of calling a constructor in member initializer list, you can conditionally call it later using placement-new.
Usually, if you don't specify a constructor in member initializer list, a default one is called. But for members of union {...};
, no constructor is called at all.
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;
}
}
Note that you have to manually call the destructor in ~LogFile()
, and you need a custom move assignment too. That's why std::variant
is better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With